View Issue Details

IDCategoryLast Update
0001343bugs2007-01-08 20:48
ReportermtahtAssigned To 
Reproducibilityrandom 
Status newResolutionopen 
Product Version 
Fixed in Version 
Summary0001343: Tranzport flakyness
Description(I note you have not updated the bugtracker to include beta8 yet as a option so I'm doing this against svn - but this is the beta8 tarball)

Is there a way to tap into usb events on a specific device? Most buttons are requiring between 1 and 3 keypresses to actually work. I also sometimes get random behavior on the shuttle, it usually ends up moving ardour to 12:20:55 or thereabouts, I can usually duplicate this behavior by wiggling the shuttle rapidly for a few seconds.

(this is on x86_64 + FC6 + a 802.11g wireless card that could maybe be causing interference?) Dropping events? bad parsing in libusb?

It's the out of the FC6 box libusb.
Additional InformationI can debug this some assuming I learn a bit more about tapping into usb events...
TagsNo tags attached.

Relationships

has duplicate 0001344 closed Tranzport feedback 

Activities

mtaht

2006-12-02 04:15

developer   ~0002837

correction this was against todays release of beta9

mtaht

2006-12-02 04:32

developer   ~0002839

K, I built the tranzport test tool from sourceforge, and all tranzport buttons respond quickly and accurately on my x86_64. This rules out wireless interference, and core library compatability issues...

I *have*, while using the datawheel, occasionally seen it return a

02 rather than a 01, and a fe, rather than a ff. I can only do it when I move the wheel really, really fast.

Do I feel like diving into the ardour source tonight? No... I'd rather make music.

But perhaps tomorrow.

mtaht

2006-12-02 05:28

developer   ~0002840

Well, ok, I put a little more time into it.

I am running the out of the box FC6 kernel at the moment (250HZ clock, rather than 1000HZ)

uncommented the printf in lib/surfaces/tranzport/tranzport_control_protocol.cc and added one to see what it did with the data wheel.

It looks to me as though you are debouncing something that doesn't need to be debounced, or events are queuing up in the driver and need to be drained..

The following only resulted in 3 changes in the state of the record state in ardour.. and was a bunch more hits on the record button than that.

I get two events sometimes for striking the record button. (on and off)

And had no relation to the "actually writing state" message which is coming from somewhere...

actually writing state
read: 00 00 00 00 01 00 00 00
read: 00 00 00 00 00 00 00 00
read: 00 00 00 00 00 00 00 00
read: 00 00 00 00 01 00 00 00
read: 00 00 00 00 01 00 00 00
read: 00 00 00 00 00 00 00 00
read: 00 00 00 00 01 00 00 00
actually writing state
read: 00 00 00 00 01 00 00 00
read: 00 00 00 00 01 00 00 00
read: 00 00 00 00 00 00 00 00
read: 00 00 00 00 01 00 00 00
read: 00 00 00 00 00 00 00 00
read: 00 00 00 00 00 00 00 00
read: 00 00 00 00 00 00 00 00
read: 00 00 00 00 01 00 00 00
actually writing state
read: 00 00 00 00 00 00 00 00

AND:

In dinking with the data wheel to get the "end up in the middle of nowhere 12:25 behavior, I don't actually see any flaws in the incoming data. Perhaps a locking problem of some kind in updating the location?

AND:

I understand now why you are advancing the audio location by such large and weird increments by the .2 stuff on the shuttle update code. I imagine doing it the way I want (by the snap bar increment) is likely to be much harder....

OK, now I'm off to make some music.

mtaht

2006-12-02 22:19

developer   ~0002845

OK, I was pleased to read up on the usbfs snoop facility in Linux. This will help a lot on another project, but at the moment I'm certain that I'm chasing a problem higher in the ardour stack than libusb.

I see this while compiling libs/surfaces/tranzport/tranzport_control_protocol.cc. If I wasn't fairly certain I was in part chasing down a thread locking problem I'd ignore it because I've seen this error so often...

libs/pbd/pbd/rcu.h: In member function ‘void** RCUManager<T>::the_pointer() const [with T = std::list<boost::shared_ptr<ARDOUR::Route>, std::allocator<boost::shared_ptr<ARDOUR::Route> > >]’:
libs/pbd/pbd/rcu.h:20: instantiated from ‘boost::shared_ptr<T> RCUManager<T>::reader() const [with T = std::list<boost::shared_ptr<ARDOUR::Route>, std::allocator<boost::shared_ptr<ARDOUR::Route> > >]’
libs/ardour/ardour/session.h:286: instantiated from here
libs/pbd/pbd/rcu.h:30: warning: dereferencing type-punned pointer will break strict-aliasing rules

And when I fall off the end of the world (ending mysteriously at 12:23:13) I saw this happen (with some of my debugging code in place)

read: 00 00 00 00 00 00 ff 00
datawheel: ff
usb screen update failed
read: 00 00 00 00 00 00 01 00
datawheel: 01
usb screen update failed
read: 00 00 00 00 00 00 ff 00
datawheel: ff
usb screen update failed
read: 00 00 00 00 00 00 01 00
datawheel: 01
usb screen update failed
read: 00 00 00 00 00 00 ff 00
datawheel: ff
usb screen update failed
read: 00 00 00 00 00 00 01 00
datawheel: 01
read: 00 00 00 00 00 00 01 00
datawheel: 01
read: 00 00 00 00 00 00 ff 00
datawheel: ff
read: 00 01 00 00 00 00 00 00

mtaht

2006-12-02 22:55

developer   ~0002846

I sure hope nobody is reading this in real time, but it's useful in tracking my state...

OK, so, (remember that I am on a non-real time enabled kernel at the moment - but I do have normal real time privs on the ardour process), the default timeouts for usb writes is 1 second - this is way too long - and still, even with that, especially when ardour2 falls off then end of the universe and ends up out at 12 hours... writes do fail. Now, this could be a priority problem in the tranzport code, and I'll look into that in a bit, but I would argue that a shorter timeout on writes (10 ms or less) and doing retries is probably best... and is what I'm going to try next.

And perhaps it makes sense to have separate reader and writer threads. Perhaps the tranzport blocks when it has a read pending or something like that?

The below is with that 1sec timeout and retries enabled... bad, bad, bad.

datawheel: ff
usb screen update failed, retrying
cmd and data were 00 01 08 32 32 3a 32 00
usb screen update failed, retrying
cmd and data were 00 01 08 32 32 3a 32 00
usb screen update failed, retrying
cmd and data were 00 01 08 32 32 3a 32 00
usb screen update failed, retrying
cmd and data were 00 01 08 32 32 3a 32 00
usb screen update failed, retrying
cmd and data were 00 01 08 32 32 3a 32 00
usb screen update failed, retrying
cmd and data were 00 01 09 39 3a 31 31 00
usb screen update failed, retrying
cmd and data were 00 01 09 39 3a 31 31 00
usb screen update failed, retrying
cmd and data were 00 01 09 39 3a 31 31 00
usb screen update failed, retrying
cmd and data were 00 01 09 39 3a 31 31 00
usb screen update failed, retrying
cmd and data were 00 01 09 39 3a 31 31 00

# with that one sec timeout it took forever to get to process these events.

read: 00 ff 00 00 00 00 00 00
read: 00 ff 00 00 00 00 00 00
read: 00 ff 00 00 00 00 00 00
read: 00 ff 00 00 00 00 00 00
read: 00 ff 00 00 00 00 00 00
read: 00 ff 00 00 00 00 00 00
read: 00 ff 00 00 00 00 00 00
read: 00 01 00 00 00 00 01 00
datawheel: 01
read: 00 01 00 00 00 00 00 00

mtaht

2006-12-02 23:39

developer   ~0002847

Oh, lovely. now that that usb snooping facility is starting to help.

With snooping on - a correct record transition seems to dump a lot of data.

Record on:

Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00
Dec 2 15:23:15 laptaht kernel: usb 2-1: data: 00 00 00 01 00 00 00 00

Record off

Dec 2 15:24:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
# yes - one line!

Now, with the record light on, and pressing record, and the event missed by ardour (light stays on), what I get back is:

Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00
Dec 2 15:26:21 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00

I've seen it go completely apeshit with the following, as well, sending thousands of these.

Dec 2 15:28:32 laptaht kernel: usb 2-1: data: 00 01 00 00 00 00 00 00

So I'm kind of pretty sure the tranzport is kind of wanting an ack of some kind.
Or my tranzport has issues.

Secondly I am thinking (but haven't proven), with the record light on another keypress of the record key usually results in:

Dec 2 15:30:45 laptaht kernel: usb 2-1: data: 00 00 00 00 01 00 00 00

Hmm... is it posible I'm getting partial reads and writes from the libusb layer... gotta go check that... brb.

mtaht

2006-12-03 01:15

developer   ~0002848

Nope, no partial reads or writes. Giving up for the day. Gonna try putting an ack in and figuring out why I get that flood of usb messages.

2006-12-03 01:16

 

debug_tranzport.patch (8,811 bytes)
*** ardour-2.0beta9.orig/libs/surfaces/tranzport/tranzport_control_protocol.cc	2006-11-02 04:27:52.000000000 -0800
--- ardour-2.0beta9/libs/surfaces/tranzport/tranzport_control_protocol.cc	2006-12-02 17:08:21.000000000 -0800
***************
*** 18,23 ****
--- 18,26 ----
      $Id: tranzport_control_protocol.cc 1059 2006-11-02 12:27:49Z paul $
  */
  
+ #define DEFAULT_USB_TIMEOUT 5
+ #define MAX_RETRY 10
+ 
  #include <iostream>
  #include <algorithm>
  #include <cmath>
*************** int
*** 429,441 ****
  TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
  {
  	int val;
  
! 	val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
  
! 	if (val < 0)
  		return val;
! 	if (val != 8)
  		return -1;
  	return 0;
  
  }	
--- 432,454 ----
  TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
  {
  	int val;
+ 	int retry = 0;
  
! 	while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
! 		printf("usb_interrupt_write failed: %d\n", val);
! 	}
  
! 	if (retry == MAX_RETRY) {
! 		printf("Too many retries on a tranzport write, aborting\n");
! 		}
! 	if (val < 0) {
! 		printf("usb_interrupt_write failed: %d\n", val);
  		return val;
! 		}
! 	if (val != 8) {
! 		printf("usb_interrupt_write failed: %d\n", val);
  		return -1;
+ 		}
  	return 0;
  
  }	
*************** TranzportControlProtocol::lcd_clear ()
*** 457,463 ****
  
  	for (uint8_t i = 0; i < 10; ++i) {
  		cmd[2] = i;
! 		usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 1000);
  	}
  	
  	memset (current_screen, ' ', sizeof (current_screen));
--- 470,476 ----
  
  	for (uint8_t i = 0; i < 10; ++i) {
  		cmd[2] = i;
! 		usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, DEFAULT_USB_TIMEOUT);
  	}
  	
  	memset (current_screen, ' ', sizeof (current_screen));
*************** TranzportControlProtocol::lights_off ()
*** 478,508 ****
  	cmd[7] = 0x00;
  
  	cmd[2] = LightRecord;
! 	if (write (cmd, 1000) == 0) {
  		lights[LightRecord] = false;
  	}
  	cmd[2] = LightTrackrec;
! 	if (write (cmd, 1000) == 0) {
  		lights[LightTrackrec] = false;
  	}
  	cmd[2] = LightTrackmute;
! 	if (write (cmd, 1000) == 0) {
  		lights[LightTrackmute] = false;
  	}
  	cmd[2] = LightTracksolo;
! 	if (write (cmd, 1000) == 0) {
  		lights[LightTracksolo] = false;
  	}
  	cmd[2] = LightAnysolo;
! 	if (write (cmd, 1000) == 0) {
  		lights[LightAnysolo] = false;
  	}
  	cmd[2] = LightLoop;
! 	if (write (cmd, 1000) == 0) {
  		lights[LightLoop] = false;
  	}
  	cmd[2] = LightPunch;
! 	if (write (cmd, 1000) == 0) {
  		lights[LightPunch] = false;
  	}
  }
--- 491,521 ----
  	cmd[7] = 0x00;
  
  	cmd[2] = LightRecord;
! 	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  		lights[LightRecord] = false;
  	}
  	cmd[2] = LightTrackrec;
! 	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  		lights[LightTrackrec] = false;
  	}
  	cmd[2] = LightTrackmute;
! 	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  		lights[LightTrackmute] = false;
  	}
  	cmd[2] = LightTracksolo;
! 	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  		lights[LightTracksolo] = false;
  	}
  	cmd[2] = LightAnysolo;
! 	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  		lights[LightAnysolo] = false;
  	}
  	cmd[2] = LightLoop;
! 	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  		lights[LightLoop] = false;
  	}
  	cmd[2] = LightPunch;
! 	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  		lights[LightPunch] = false;
  	}
  }
*************** TranzportControlProtocol::light_on (Ligh
*** 523,529 ****
  		cmd[6] = 0x00;
  		cmd[7] = 0x00;
  
! 		if (write (cmd, 1000) == 0) {
  			lights[light] = true;
  			return 0;
  		} else {
--- 536,542 ----
  		cmd[6] = 0x00;
  		cmd[7] = 0x00;
  
! 		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  			lights[light] = true;
  			return 0;
  		} else {
*************** TranzportControlProtocol::light_off (Lig
*** 551,557 ****
  		cmd[6] = 0x00;
  		cmd[7] = 0x00;
  
! 		if (write (cmd, 1000) == 0) {
  			lights[light] = false;
  			return 0;
  		} else {
--- 564,570 ----
  		cmd[6] = 0x00;
  		cmd[7] = 0x00;
  
! 		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
  			lights[light] = false;
  			return 0;
  		} else {
*************** TranzportControlProtocol::monitor_work (
*** 598,610 ****
  			first_time = true;
  		}
  
! 		pthread_testcancel();
  		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
  		pthread_testcancel();
  
  		if (val == 8) {
  			process (buf);
! 		}
  
  		if (_device_status != STATUS_OFFLINE) {
  			if (first_time) {
--- 611,624 ----
  			first_time = true;
  		}
  
! 		pthread_testcancel(); // I have no idea what pthread_testcancel really does
! 
  		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
  		pthread_testcancel();
  
  		if (val == 8) {
  			process (buf);
! 		} else if (val > 0) { printf("Partial usb read: %d\n", val); } 
  
  		if (_device_status != STATUS_OFFLINE) {
  			if (first_time) {
*************** TranzportControlProtocol::monitor_work (
*** 613,619 ****
--- 627,635 ----
  				first_time = false;
  			}
  			/* update whatever needs updating */
+ 	//		printf("In update_state\n");
  			update_state ();
+ 	//		printf("out of update_state\n");
  		}
  	}
  
*************** TranzportControlProtocol::update_state (
*** 627,632 ****
--- 643,650 ----
  	int col_base;
  	int col;
  	int cell;
+ 	int retry;
+ 	int val;
  
  	/* do the text updates */
  
*************** TranzportControlProtocol::update_state (
*** 664,674 ****
  				cmd[5] = pending_screen[row][col_base+2];
  				cmd[6] = pending_screen[row][col_base+3];
  				cmd[7] = 0x00;
! 
! 				if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 1000) == 8) {
  					/* successful write: copy to current */
  					memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
! 				}
  
  				/* skip the rest of the 4 character cell since we wrote+copied it already */
  				
--- 682,699 ----
  				cmd[5] = pending_screen[row][col_base+2];
  				cmd[6] = pending_screen[row][col_base+3];
  				cmd[7] = 0x00;
! 				retry=0;
! 				while (((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, DEFAULT_USB_TIMEOUT)) != 8) && retry++ < MAX_RETRY)  {
! 					printf("usb screen update failed, val = %d, retrying\n", val); 
! 					printf("cmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); 
! 				} 
! 				if(retry == MAX_RETRY) {
! 					printf("usb screen update failed for some reason... why? cmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); 
! 					}
! // pretend it worked anyway for now
  					/* successful write: copy to current */
   				memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
! 				retry = 0;
  
  				/* skip the rest of the 4 character cell since we wrote+copied it already */
  				
*************** TranzportControlProtocol::update_state (
*** 814,820 ****
  int
  TranzportControlProtocol::process (uint8_t* buf)
  {
! 	// printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
  
  	uint32_t this_button_mask;
  	uint32_t button_changes;
--- 839,845 ----
  int
  TranzportControlProtocol::process (uint8_t* buf)
  {
! 	printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
  
  	uint32_t this_button_mask;
  	uint32_t button_changes;
*************** TranzportControlProtocol::process (uint8
*** 831,836 ****
--- 856,862 ----
  	buttonmask = this_button_mask;
  
  	if (_datawheel) {
+ 	printf("datawheel: %02x\n", _datawheel);
  		datawheel ();
  	}
  
*************** TranzportControlProtocol::process (uint8
*** 863,868 ****
--- 889,895 ----
  		}
  	}
  	if (button_changes & ButtonTrackRec) {
+ 		printf("button trackrec: %d button record %d\n", button_changes, ButtonTrackRec);
  		if (buttonmask & ButtonTrackRec) {
  			button_event_trackrec_press (buttonmask&ButtonShift);
  		} else {
*************** TranzportControlProtocol::process (uint8
*** 968,973 ****
--- 995,1001 ----
  		}
  	}
  	if (button_changes & ButtonRecord) {
+ 		printf("button changes: %d button record %d\n", button_changes, ButtonRecord);
  		if (buttonmask & ButtonRecord) {
  			button_event_record_press (buttonmask&ButtonShift);
  		} else {
*************** TranzportControlProtocol::button_event_t
*** 1036,1041 ****
--- 1064,1070 ----
  	if (shifted) {
  		toggle_all_rec_enables ();
  	} else {
+ 	  printf("Rec enable called with: %d\n",shifted);
  		route_set_rec_enable (0, !route_get_rec_enable (0));
  	}
  }
debug_tranzport.patch (8,811 bytes)

mtaht

2006-12-03 01:29

developer   ~0002849

I put my debugging hacks up as a patch against beta9 in the hope they would be useful. Also useful is turning on usb snooping.

echo 1 > /sys/module/usbcore/parameters/usbfs_snoop

I am still pretty much where I started. 3 preliminary theories:

1) The tranzport pretty much requires a write after certain kinds of reads. Also, the real state machine is more complex than in the ardour code, not only is the tranzport basically a keyboard with 22 shift keys (in my tests, all keys can be pressed in combination), key down/up behavior seems to be tied to previous events somehow. (or I'm missing getting all the data all the time)

2) Some portions of the code are not adaquately or correctly threaded.

3) I don't trust libusb to be fast enough or properly threaded. IRQ handlers belong in the kernel, dang it.

Next up:

Trying this on x86 rather than x86_64
Putting locks around some of the critical code sections
Getting a better grip on the state machine transitions via the tranzport utility.

... after I go make some music.

mtaht

2006-12-03 06:17

developer   ~0002850

I moved to a RT enabled kernel (2.6.18-rt7)...

and the tranzport started working MUCH better. IRQ handlers in userspace is much more doable with rt.

I will FAQ this - "My tranzport isn't working" - rt kernel required...

I will have a small patch later that does do some more error checking and a couple retries but I just had a very enjoyable hour with the tranzport and beta9 otherwise.

Looking at the code gave me a good grip on everything that can be done with the tranzport and I like IT....

Back to the jam.

mtaht

2006-12-03 16:31

developer   ~0002852

Last edited: 2006-12-03 17:35

API change proposal:

For certain kinds of writes (updating the lights, updating some portion of the display, like "track") we care that the data actually makes it to the tranzport.

For other kinds of writes (timecode update) we don't.

The default timeout of 1 sec tends to make the unit unresponsive when things are flaky (or when running under an unoptimized kernel). I still see writes fail occasionally on an optimized kernel, as well.

In the long term I can see having reads and writes be in separate threads; the reader thread could note that the tranzport is disconnected and set a flag stopping overall writes.

In the short term perhaps having an optional argument to ::write to set a flag in the screen update ('required') would be good and forcing X number of retries on writes to that area.

Secondly abstracting out a reliable usb_interrupt_write and calling that for button updates (write_retry to try harder, or write_reliable, to keep trying), would probably be good.

Continuing to daydream... being able to temporarily "flash" an update to the screen for a second or two would be good, specifically in the case of "undo", but could also be used to flash informational messages like "Rewinding" (maybe a bit much that)

And showing beats rather than smte

Flashing the record light according to the tempo, or the rec/mute/solo/any solo lights according to the tempo...

Moving the display by the snap to bar setting, or shifting markers by same

tuning a loop...

tie ins to ladspa - Graphic eq, peak meter, tuner

Editing names of of tracks or markers

Shifting markers back and forth
 
Ahh, what fun.

paul

2006-12-05 16:27

administrator   ~0002865

at some point i'll get back onto this, but will note this: I did a lot of debugging of tranzport flakiness myself, and my conclusion was that the design of libusb is completely inadequate for this kind of device. this is mostly why i haven't gone back to work on higher level stuff.

mtaht

2006-12-05 17:39

developer   ~0002869

I agree that libusb is inadaquate (but unfortunately portable)

... But I'll keep hacking at it because it's A) so cool, and B) means I don't have to understand much of the rest of the ardour code.

At the moment I have something that is a bit better than what is in svn, when I'm happier with it I'll put up a patch.

mtaht

2006-12-05 17:42

developer   ~0002870

I'm also still kind of convinced there is locking issue in updating the location of ardour in the audio file, I can still (via jiggling the shuttle wheel frantically) put ardour out at the 12+ hour mark....

mtaht

2006-12-26 04:49

developer   ~0002951

Last edited: 2006-12-27 07:38

re: transport_02.patch

Bug fixes:

Some code paths simplified.

Unnessessary divide eliminated.

Pressing shift backlight forces a screen redraw on button release. Usually.

Track names are now padded to 8 characters.

When writes fail, they are retried.

When retries are exausted, they are retried on the next processing pass,
in most cases.

Dubious fixes

rtpriv bumped to 50

Still to come:

MORE RELIABILITY FIXES, more profiling and fiddling with timeouts, more arches....

PAN display.

Marker shuttle mode (slide the most recent marker around with the shuttle)

What was your plan for the gap between the track name and db meter?

Undo/Redo feedback

The nice meter mode should probably write through the buffer, too.

I want to move the flush into it's own routine "flush". Partially because it's cleaner, and partially because I am thinking of moving to reader/writer threads.

- also move the button writes prior to the screen writes as I prefer
immediate feedback on buttons to a faster display (and less failures
that way, I think)

Known problems: 1) End tranzport shutdown

"Programming error: ControlProtocolManager::teardown() called for Tranzport, but it was not found in control_protocol_info". I currently have no idea what
this means, only that:

a) When the tranzport loses its connection, restarting the tranzport usually
takes 2 tries, and not always then.

b) the tranzport seems to lose its connection a lot. Lots of bad things
happen then.

2) I really want to stop processing writes as soon as there is a failure
and go back to reads.

3) In shuttle mode there's an issue I forget. (too much eggnog)

4) bool lights[7]; bool flash_lights[7]; bool pending_lights[7]. Just
   bothers me to waste that much memory. I know it's trivial, but...

5) Only when going forwards on hitting the track buttons we sometimes
end up on a ------- track. Seemingly never on going backward. Something is busted in next_track();

There are a few debugging vars and printfs left behind right now,
and flashing the lights is unimplemented, but arguably this version
works better than the one in svn.

2006-12-26 05:05

 

transport_02.patch (10,505 bytes)
Index: tranzport_control_protocol.h
===================================================================
--- tranzport_control_protocol.h	(revision 1247)
+++ tranzport_control_protocol.h	(working copy)
@@ -102,8 +102,10 @@
 	Glib::Mutex update_lock;
 	char current_screen[2][20];
 	char pending_screen[2][20];
+	char flash_screen[2][20];
 	bool lights[7];
 	bool pending_lights[7];
+	bool flash_lights[7];
 
 	bool           last_negative;
 	uint32_t       last_hrs;
@@ -121,12 +123,14 @@
 	int open ();
 	int read (uint32_t timeout_override = 0);
 	int write (uint8_t* cmd, uint32_t timeout_override = 0);
+	int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
 	int close ();
 
 	int open_core (struct usb_device*);
 
 	void lcd_clear ();
 	void print (int row, int col, const char* text);
+	void print_noretry (int row, int col, const char* text);
 	int  light_on (LightID);
 	int  light_off (LightID);
 	void lights_off ();
Index: tranzport_control_protocol.cc
===================================================================
--- tranzport_control_protocol.cc	(revision 1247)
+++ tranzport_control_protocol.cc	(working copy)
@@ -18,6 +18,27 @@
     $Id$
 */
 
+/* Design notes: The tranzport is a unique device, basically a 
+   20 lcd gui with 22 shift keys and 8 blinking lights. 
+   You can think of it as an altair for music.
+
+   It might be good one day, to break the gui, keyboard, and blinking light
+   components into separate parts, but for now, this remains monolithic.
+
+   As such it has several unique constraints. The device exerts flow control
+   by having a usb write fail. It is pointless to retry madly at that point,
+   the device is busy, and it's not going to become unbusy very quickly. 
+
+   So writes need to be either "mandatory" or "unreliable", and therein 
+   lies the rub.
+
+   mike.taht@gmail.com
+ */
+
+#define DEFAULT_USB_TIMEOUT 20
+#define MAX_RETRY 1
+#define DEBUG_TRANZPORT 1
+
 #include <iostream>
 #include <algorithm>
 #include <cmath>
@@ -75,7 +96,7 @@
 
 	set_route_table_size (1);
 	
-	timeout = 60000;
+	timeout = 6000;
 	buttonmask = 0;
 	_datawheel = 0;
 	_device_status = STATUS_OFFLINE;
@@ -92,6 +113,7 @@
 
 	memset (current_screen, 0, sizeof (current_screen));
 	memset (pending_screen, 0, sizeof (pending_screen));
+	memset (flash_screen, 0, sizeof (flash_screen));
 
 	for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) {
 		lights[i] = false;
@@ -149,9 +171,9 @@
 		} else {
 			cerr << "Begin tranzport shutdown\n";
 			pthread_cancel_one (thread);
-			cerr << "Thread dead\n";
-			// lcd_clear ();
-			// lights_off ();
+			cerr << "Tranzport Thread dead\n";
+			lcd_clear ();
+			lights_off ();
 			// cerr << "dev reset\n";
 			close ();
 			_active = false;
@@ -220,7 +242,6 @@
 	lights_off ();
 	show_current_track ();
 	show_wheel_mode ();
-	show_wheel_mode ();
 	show_transport_time ();
 	display_mode = DisplayNormal;
 }
@@ -230,10 +251,12 @@
 log_meter (float db)
 {
 	float def = 0.0f; /* Meter deflection %age */
- 
-	if (db < -70.0f) {
-		def = 0.0f;
-	} else if (db < -60.0f) {
+	/* avoid a divide */
+
+	if (db < -70.0f) return 0.0f;
+ 	if (db > 6.0f) return 1.0f;
+
+	if (db < -60.0f) {
 		def = (db + 70.0f) * 0.25f;
 	} else if (db < -50.0f) {
 		def = (db + 60.0f) * 0.5f + 2.5f;
@@ -245,8 +268,6 @@
 		def = (db + 30.0f) * 2.0f + 30.0f;
 	} else if (db < 6.0f) {
 		def = (db + 20.0f) * 2.5f + 50.0f;
-	} else {
-		def = 115.0f;
 	}
 	
 	/* 115 is the deflection %age that would be 
@@ -343,7 +364,7 @@
 		print (1, 15, buf);
 
 		sprintf (buf, "%02" PRIu32, smpte.frames);
-		print (1, 18, buf);
+		print_noretry (1, 18, buf); 
 
 		last_where = where;
 	}
@@ -426,16 +447,44 @@
 }
 	
 int
-TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
 {
 	int val;
 
 	val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
+	if (val < 0) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return val;
+		}
+	if (val != 8) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return -1;
+		}
+	return 0;
 
-	if (val < 0)
+}	
+
+int
+TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+{
+	int val;
+	int retry = 0;
+
+	while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
+		printf("usb_interrupt_write failed, retrying: %d\n", val);
+	}
+
+	if (retry == MAX_RETRY) {
+		printf("Too many retries on a tranzport write, aborting\n");
+		}
+	if (val < 0) {
+		printf("usb_interrupt_write failed: %d\n", val);
 		return val;
-	if (val != 8)
+		}
+	if (val != 8) {
+		printf("usb_interrupt_write failed: %d\n", val);
 		return -1;
+		}
 	return 0;
 
 }	
@@ -457,11 +506,12 @@
 
 	for (uint8_t i = 0; i < 10; ++i) {
 		cmd[2] = i;
-		usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 1000);
+		write (cmd, DEFAULT_USB_TIMEOUT);
 	}
 	
-	memset (current_screen, ' ', sizeof (current_screen));
+ 	memset (current_screen, ' ', sizeof (current_screen));
 	memset (pending_screen, ' ', sizeof (pending_screen));
+	memset (flash_screen, ' ', sizeof (pending_screen));
 }
 
 void
@@ -478,31 +528,31 @@
 	cmd[7] = 0x00;
 
 	cmd[2] = LightRecord;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightRecord] = false;
 	}
 	cmd[2] = LightTrackrec;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackrec] = false;
 	}
 	cmd[2] = LightTrackmute;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackmute] = false;
 	}
 	cmd[2] = LightTracksolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTracksolo] = false;
 	}
 	cmd[2] = LightAnysolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightAnysolo] = false;
 	}
 	cmd[2] = LightLoop;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightLoop] = false;
 	}
 	cmd[2] = LightPunch;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightPunch] = false;
 	}
 }
@@ -523,7 +573,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = true;
 			return 0;
 		} else {
@@ -551,7 +601,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = false;
 			return 0;
 		} else {
@@ -571,11 +621,13 @@
 	uint8_t buf[8];
 	int val;
 	bool first_time = true;
+	int pending = 0;
+	uint8_t offline = 0;
 
 	PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
 
 	memset (&rtparam, 0, sizeof (rtparam));
-	rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
+	rtparam.sched_priority = 50; /* XXX should be relative to audio (JACK) thread */
 	
 	if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
 		// do we care? not particularly.
@@ -594,12 +646,15 @@
 		/* anything to read ? */
 
 		if (_device_status == STATUS_OFFLINE) {
-			light_off (LightRecord);
-			first_time = true;
+		  if(offline == 1) { 
+		  cerr << "Transport has gone offline\n";
+		  light_off (LightRecord); // do we really want to keep poking the device while it's toast?
+		  }
+		  first_time = true;
 		}
 
 		pthread_testcancel();
-		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
+		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 20);
 		pthread_testcancel();
 
 		if (val == 8) {
@@ -611,9 +666,12 @@
 				lcd_clear ();
 				lights_off ();
 				first_time = false;
+				offline = 0;
 			}
 			/* update whatever needs updating */
-			update_state ();
+			pending = update_state ();
+		} else {
+		  offline++;
 		}
 	}
 
@@ -627,6 +685,9 @@
 	int col_base;
 	int col;
 	int cell;
+	int retry;
+	int val;
+	int pending = 0;
 
 	/* do the text updates */
 
@@ -647,7 +708,6 @@
 	for (row = 0; row < 2; ++row) {
 
 		for (col_base = 0, col = 0; col < 20; ) {
-			
 			if (pending_screen[row][col] != current_screen[row][col]) {
 
 				/* something in this cell is different, so dump the cell
@@ -664,10 +724,14 @@
 				cmd[5] = pending_screen[row][col_base+2];
 				cmd[6] = pending_screen[row][col_base+3];
 				cmd[7] = 0x00;
-
-				if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 1000) == 8) {
+				retry= write(cmd);
+				if(retry == -(MAX_RETRY)) {
+				  /* try to update this cell on the next go-round */
+					printf("usb screen update failed for some reason... why? cmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); 
+					pending += 1;
+				} else {
 					/* successful write: copy to current */
-					memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
+				  memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
 				}
 
 				/* skip the rest of the 4 character cell since we wrote+copied it already */
@@ -808,7 +872,7 @@
 		}
 	}
 
-	return 0;
+	return pending;
 }
 
 int
@@ -981,11 +1045,20 @@
 void
 TranzportControlProtocol::show_current_track ()
 {
-	if (route_table[0] == 0) {
-		print (0, 0, "--------");
-	} else {
-		print (0, 0, route_get_name (0).substr (0, 8).c_str());
-	}
+  char pad[9];
+  char *v;
+  int len;
+  if (route_table[0] == 0) {
+    print (0, 0, "--------");
+    last_track_gain = -999.0;
+  } else {
+    strcpy(pad,"        ");
+    v =  (char *)route_get_name (0).substr (0, 8).c_str();
+    if((len = strlen(v)) > 0) {
+      strncpy(pad,(char *)v,len);
+    }
+    print (0, 0, pad);
+  }
 }
 
 void
@@ -1006,6 +1079,11 @@
 void
 TranzportControlProtocol::button_event_backlight_release (bool shifted)
 {
+	if (shifted) {
+	   	memset (current_screen, ' ', sizeof (current_screen));
+		last_track_gain = FLT_MAX;
+		normal_update(); //  redraw_screen();  
+	}
 }
 
 void
@@ -1523,8 +1601,13 @@
 	print (1, 0, text.c_str());
 }
 
+	void
+TranzportControlProtocol::print (int row, int col, const char *text) {
+	print_noretry(row,col,text);
+}
+
 void
-TranzportControlProtocol::print (int row, int col, const char *text)
+TranzportControlProtocol::print_noretry (int row, int col, const char *text)
 {
 	int cell;
 	uint32_t left = strlen (text);
transport_02.patch (10,505 bytes)

2006-12-26 18:46

 

tranzport03.patch (14,226 bytes)
Index: tranzport_control_protocol.h
===================================================================
--- tranzport_control_protocol.h	(revision 1247)
+++ tranzport_control_protocol.h	(working copy)
@@ -72,7 +72,8 @@
 	enum WheelShiftMode {
 		WheelShiftGain,
 		WheelShiftPan,
-		WheelShiftMaster
+		WheelShiftMaster,
+		WheelShiftMarker
 	};
 		
 	enum WheelMode {
@@ -102,8 +103,10 @@
 	Glib::Mutex update_lock;
 	char current_screen[2][20];
 	char pending_screen[2][20];
+	char flash_screen[2][20];
 	bool lights[7];
 	bool pending_lights[7];
+	bool flash_lights[7];
 
 	bool           last_negative;
 	uint32_t       last_hrs;
@@ -121,12 +124,14 @@
 	int open ();
 	int read (uint32_t timeout_override = 0);
 	int write (uint8_t* cmd, uint32_t timeout_override = 0);
+	int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
 	int close ();
 
 	int open_core (struct usb_device*);
 
 	void lcd_clear ();
 	void print (int row, int col, const char* text);
+	void print_noretry (int row, int col, const char* text);
 	int  light_on (LightID);
 	int  light_off (LightID);
 	void lights_off ();
@@ -141,6 +146,8 @@
 	void show_current_track ();
 	void show_track_gain ();
 	void show_transport_time ();
+	void show_bbt (nframes_t where);
+	void show_smpte (nframes_t where);
 	void show_wheel_mode ();
 	void show_gain ();
 	void show_pan ();
@@ -208,6 +215,9 @@
 
 	int process (uint8_t *);
 	int update_state();
+	int flush();
+	int flush_buttons();
+	int flush_screen();
 };
 
 
Index: tranzport_control_protocol.cc
===================================================================
--- tranzport_control_protocol.cc	(revision 1247)
+++ tranzport_control_protocol.cc	(working copy)
@@ -18,6 +18,27 @@
     $Id$
 */
 
+/* Design notes: The tranzport is a unique device, basically a 
+   20 lcd gui with 22 shift keys and 8 blinking lights. 
+   You can think of it as an altair for music.
+
+   It might be good one day, to break the gui, keyboard, and blinking light
+   components into separate parts, but for now, this remains monolithic.
+
+   As such it has several unique constraints. The device exerts flow control
+   by having a usb write fail. It is pointless to retry madly at that point,
+   the device is busy, and it's not going to become unbusy very quickly. 
+
+   So writes need to be either "mandatory" or "unreliable", and therein 
+   lies the rub.
+
+   mike.taht@gmail.com
+ */
+
+#define DEFAULT_USB_TIMEOUT 10
+#define MAX_RETRY 1
+#define DEBUG_TRANZPORT 1
+
 #include <iostream>
 #include <algorithm>
 #include <cmath>
@@ -33,6 +54,7 @@
 #include <ardour/route.h>
 #include <ardour/audio_track.h>
 #include <ardour/session.h>
+#include <ardour/tempo.h>
 #include <ardour/location.h>
 #include <ardour/dB.h>
 
@@ -75,7 +97,7 @@
 
 	set_route_table_size (1);
 	
-	timeout = 60000;
+	timeout = 6000;
 	buttonmask = 0;
 	_datawheel = 0;
 	_device_status = STATUS_OFFLINE;
@@ -92,6 +114,7 @@
 
 	memset (current_screen, 0, sizeof (current_screen));
 	memset (pending_screen, 0, sizeof (pending_screen));
+	memset (flash_screen, 0, sizeof (flash_screen));
 
 	for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) {
 		lights[i] = false;
@@ -100,6 +123,10 @@
 	for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) {
 		pending_lights[i] = false;
 	}
+
+	for (uint32_t i = 0; i < sizeof(flash_lights)/sizeof(flash_lights[0]); ++i) {
+		flash_lights[i] = false;
+	}
 }
 
 TranzportControlProtocol::~TranzportControlProtocol ()
@@ -149,9 +176,9 @@
 		} else {
 			cerr << "Begin tranzport shutdown\n";
 			pthread_cancel_one (thread);
-			cerr << "Thread dead\n";
-			// lcd_clear ();
-			// lights_off ();
+			cerr << "Tranzport Thread dead\n";
+			lcd_clear ();
+			lights_off ();
 			// cerr << "dev reset\n";
 			close ();
 			_active = false;
@@ -168,7 +195,7 @@
 	if (route_table[0]) {
 		gain_t g = route_get_gain (0);
 		if (g != last_track_gain) {
-			char buf[16];
+			char buf[9];
 			snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
 			print (0, 9, buf); 
 			last_track_gain = g;
@@ -219,9 +246,9 @@
 	lcd_clear ();
 	lights_off ();
 	show_current_track ();
+	show_transport_time ();
+	show_track_gain ();
 	show_wheel_mode ();
-	show_wheel_mode ();
-	show_transport_time ();
 	display_mode = DisplayNormal;
 }
 
@@ -230,10 +257,11 @@
 log_meter (float db)
 {
 	float def = 0.0f; /* Meter deflection %age */
- 
-	if (db < -70.0f) {
-		def = 0.0f;
-	} else if (db < -60.0f) {
+
+	if (db < -70.0f) return 0.0f;
+ 	if (db > 6.0f) return 1.0f;
+
+	if (db < -60.0f) {
 		def = (db + 70.0f) * 0.25f;
 	} else if (db < -50.0f) {
 		def = (db + 60.0f) * 0.5f + 2.5f;
@@ -245,8 +273,6 @@
 		def = (db + 30.0f) * 2.0f + 30.0f;
 	} else if (db < 6.0f) {
 		def = (db + 20.0f) * 2.5f + 50.0f;
-	} else {
-		def = 115.0f;
 	}
 	
 	/* 115 is the deflection %age that would be 
@@ -267,6 +293,8 @@
 	float level = route_get_peak_input_power (0, 0);
 	float fraction = log_meter (level);
 
+	/* Someday add a peak bar*/
+
 	/* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
 	   the screen is 20 chars wide, so we can display 40 different levels. compute the level,
 	   then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
@@ -318,10 +346,36 @@
 }
 
 void
+TranzportControlProtocol::show_bbt (nframes_t where)
+{ 
+	if (where != last_where) {
+	  char buf[16];
+	  BBT_Time bbt;
+	  session->tempo_map().bbt_time (where, bbt);
+	  sprintf (buf, "%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
+	  print (1, 9, buf);
+	  last_where = where;
+
+	  //        if (bbt_upper_info_label) {
+	  //     TempoMap::Metric m (session->tempo_map().metric_at (when));
+	  //     sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
+          //      bbt_lower_info_label->set_text (buf);
+          //      sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
+          //      bbt_upper_info_label->set_text (buf);
+        }
+}
+
+
+void
 TranzportControlProtocol::show_transport_time ()
 {
 	nframes_t where = session->transport_frame();
-	
+	show_bbt(where);
+}	
+
+void
+TranzportControlProtocol::show_smpte (nframes_t where)
+{
 	if (where != last_where) {
 
 		char buf[5];
@@ -343,7 +397,7 @@
 		print (1, 15, buf);
 
 		sprintf (buf, "%02" PRIu32, smpte.frames);
-		print (1, 18, buf);
+		print_noretry (1, 18, buf); 
 
 		last_where = where;
 	}
@@ -426,20 +480,93 @@
 }
 	
 int
-TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
 {
 	int val;
 
 	val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
 
-	if (val < 0)
+	if (val < 0) {
+		printf("usb_interrupt_write failed: %d\n", val);
 		return val;
-	if (val != 8)
+		}
+
+	if (val != 8) {
+		printf("usb_interrupt_write failed: %d\n", val);
 		return -1;
+		}
+
 	return 0;
 
 }	
 
+int
+TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+{
+	int val;
+	int retry = 0;
+
+	while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
+		printf("usb_interrupt_write failed, retrying: %d\n", val);
+	}
+
+	if (retry == MAX_RETRY) {
+		printf("Too many retries on a tranzport write, aborting\n");
+		}
+
+	if (val < 0) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return val;
+		}
+	if (val != 8) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return -1;
+		}
+	return 0;
+
+}	
+
+
+// We have a state "Unknown"
+// We have another state - no_retry. Misleading, as we still retry on the next pass
+// We have an "displayed" screen
+// We always draw into the pending screen
+// We have an active screen
+// Print arg - we have 
+// setactive
+// flush
+
+/*
+flash_screen.clear();
+flash_screen.print(0,0,"Undone:"); // Someday pull the undo stack from somewhere
+flash_screen.print(1,0,"Nextup:"); 
+
+if(flash_messages && lcd.getactive() != flash_screen) lcd.setactive(flash_screen,2000);
+
+screen::setactive(screen_name,duration); // duration in ms
+screen::getactive();
+*/
+
+
+int
+TranzportControlProtocol::flush ()
+{
+    return flush_buttons() || flush_screen(); 
+}
+ 
+int
+TranzportControlProtocol::flush_buttons ()
+{
+  return 0;
+}
+
+int
+TranzportControlProtocol::flush_screen ()
+{
+  return 0;
+}
+
+
 void
 TranzportControlProtocol::lcd_clear ()
 {
@@ -457,11 +584,12 @@
 
 	for (uint8_t i = 0; i < 10; ++i) {
 		cmd[2] = i;
-		usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 1000);
+		write (cmd, DEFAULT_USB_TIMEOUT);
 	}
 	
-	memset (current_screen, ' ', sizeof (current_screen));
+ 	memset (current_screen, ' ', sizeof (current_screen));
 	memset (pending_screen, ' ', sizeof (pending_screen));
+	memset (flash_screen, ' ', sizeof (pending_screen));
 }
 
 void
@@ -478,31 +606,31 @@
 	cmd[7] = 0x00;
 
 	cmd[2] = LightRecord;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightRecord] = false;
 	}
 	cmd[2] = LightTrackrec;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackrec] = false;
 	}
 	cmd[2] = LightTrackmute;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackmute] = false;
 	}
 	cmd[2] = LightTracksolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTracksolo] = false;
 	}
 	cmd[2] = LightAnysolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightAnysolo] = false;
 	}
 	cmd[2] = LightLoop;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightLoop] = false;
 	}
 	cmd[2] = LightPunch;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightPunch] = false;
 	}
 }
@@ -523,7 +651,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = true;
 			return 0;
 		} else {
@@ -551,7 +679,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = false;
 			return 0;
 		} else {
@@ -571,11 +699,13 @@
 	uint8_t buf[8];
 	int val;
 	bool first_time = true;
+	int pending = 0;
+	uint8_t offline = 0;
 
 	PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
 
 	memset (&rtparam, 0, sizeof (rtparam));
-	rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
+	rtparam.sched_priority = 50; /* XXX should be relative to audio (JACK) thread */
 	
 	if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
 		// do we care? not particularly.
@@ -594,12 +724,15 @@
 		/* anything to read ? */
 
 		if (_device_status == STATUS_OFFLINE) {
-			light_off (LightRecord);
-			first_time = true;
+		  if(offline == 1) { 
+		  cerr << "Transport has gone offline\n";
+		  light_off (LightRecord); // do we really want to keep poking the device while it's toast?
+		  }
+		  first_time = true;
 		}
 
 		pthread_testcancel();
-		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
+		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 20);
 		pthread_testcancel();
 
 		if (val == 8) {
@@ -611,9 +744,12 @@
 				lcd_clear ();
 				lights_off ();
 				first_time = false;
+				offline = 0;
 			}
 			/* update whatever needs updating */
-			update_state ();
+			pending = update_state ();
+		} else {
+		  offline++;
 		}
 	}
 
@@ -627,6 +763,9 @@
 	int col_base;
 	int col;
 	int cell;
+	int retry;
+	int val;
+	int pending = 0;
 
 	/* do the text updates */
 
@@ -647,7 +786,6 @@
 	for (row = 0; row < 2; ++row) {
 
 		for (col_base = 0, col = 0; col < 20; ) {
-			
 			if (pending_screen[row][col] != current_screen[row][col]) {
 
 				/* something in this cell is different, so dump the cell
@@ -664,10 +802,14 @@
 				cmd[5] = pending_screen[row][col_base+2];
 				cmd[6] = pending_screen[row][col_base+3];
 				cmd[7] = 0x00;
-
-				if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 1000) == 8) {
+				retry= write(cmd);
+				if(retry == -(MAX_RETRY)) {
+				  /* try to update this cell on the next go-round */
+					printf("usb screen update failed for some reason... why? cmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); 
+					pending += 1;
+				} else {
 					/* successful write: copy to current */
-					memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
+				  memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
 				}
 
 				/* skip the rest of the 4 character cell since we wrote+copied it already */
@@ -808,7 +950,7 @@
 		}
 	}
 
-	return 0;
+	return pending;
 }
 
 int
@@ -981,11 +1123,20 @@
 void
 TranzportControlProtocol::show_current_track ()
 {
-	if (route_table[0] == 0) {
-		print (0, 0, "--------");
-	} else {
-		print (0, 0, route_get_name (0).substr (0, 8).c_str());
-	}
+  char pad[9];
+  char *v;
+  int len;
+  if (route_table[0] == 0) {
+    print (0, 0, "--------");
+    last_track_gain = -999.0;
+  } else {
+    strcpy(pad,"        ");
+    v =  (char *)route_get_name (0).substr (0, 8).c_str();
+    if((len = strlen(v)) > 0) {
+      strncpy(pad,(char *)v,len);
+    }
+    print (0, 0, pad);
+  }
 }
 
 void
@@ -1006,6 +1157,11 @@
 void
 TranzportControlProtocol::button_event_backlight_release (bool shifted)
 {
+	if (shifted) {
+	   	memset (current_screen, ' ', sizeof (current_screen));
+		last_track_gain = FLT_MAX;
+		normal_update(); //  redraw_screen();  
+	}
 }
 
 void
@@ -1523,8 +1679,13 @@
 	print (1, 0, text.c_str());
 }
 
+	void
+TranzportControlProtocol::print (int row, int col, const char *text) {
+	print_noretry(row,col,text);
+}
+
 void
-TranzportControlProtocol::print (int row, int col, const char *text)
+TranzportControlProtocol::print_noretry (int row, int col, const char *text)
 {
 	int cell;
 	uint32_t left = strlen (text);
tranzport03.patch (14,226 bytes)

mtaht

2006-12-26 18:56

developer   ~0002952

tranzport03.patch:

I got bars/beat display working (temporarily overrriding the smpte display).

After a couple hours of fiddling with the current code I'll say it works better in the general case but fails more miserably in failure cases. This version of the code has a really hard time getting the tranzport to init, and it drops offline a lot, too. (might not be code but battery)

I am looking over lcdproc for inspiration on a general purpose lcd writer class. The idea of getting lcdproc working is cool, I could have a lcd on a machine anywhere else on the network getting updates. I started writing one but realized I still don't grok templates well enough to do it right.

I am looking over sweetwater sound for more surfaces to play with....

I also realized last night that a "surface" could be *anything* - example: a handhelds.org ipaq running X could also serve as a remote controller. Didn't know that gtk supported having multiple displays at this point - bwahahahahaha! I already run ardour and related apps across multiple X window displays....

mtaht

2006-12-26 19:25

developer   ~0002953

Last edited: 2006-12-27 07:22

Correction against prior report.

hitting the track buttons we sometimes end up on a ------- track, when there aren't any. happens when going forward only, randomly.

2006-12-27 07:25

 

tranzport_04.patch (24,808 bytes)
Index: libs/surfaces/tranzport/tranzport_control_protocol.cc
===================================================================
--- libs/surfaces/tranzport/tranzport_control_protocol.cc	(revision 1247)
+++ libs/surfaces/tranzport/tranzport_control_protocol.cc	(working copy)
@@ -18,6 +18,26 @@
     $Id$
 */
 
+/* Design notes: The tranzport is a unique device, basically a 
+   20 lcd gui with 22 shift keys and 8 blinking lights. 
+
+   It might be good one day, to break the gui, keyboard, and blinking light
+   components into separate parts, but for now, this remains monolithic.
+
+   As such it has several unique constraints. The device exerts flow control
+   by having a usb write fail. It is pointless to retry madly at that point,
+   the device is busy, and it's not going to become unbusy very quickly. 
+
+   So writes need to be either "mandatory" or "unreliable", and therein 
+   lies the rub.
+
+   mike.taht@gmail.com
+ */
+
+#define DEFAULT_USB_TIMEOUT 100
+#define MAX_RETRY 0
+#define DEBUG_TRANZPORT 1
+
 #include <iostream>
 #include <algorithm>
 #include <cmath>
@@ -33,6 +53,7 @@
 #include <ardour/route.h>
 #include <ardour/audio_track.h>
 #include <ardour/session.h>
+#include <ardour/tempo.h>
 #include <ardour/location.h>
 #include <ardour/dB.h>
 
@@ -75,7 +96,7 @@
 
 	set_route_table_size (1);
 	
-	timeout = 60000;
+	timeout = 6000;
 	buttonmask = 0;
 	_datawheel = 0;
 	_device_status = STATUS_OFFLINE;
@@ -89,10 +110,24 @@
 	last_track_gain = FLT_MAX;
 	display_mode = DisplayNormal;
 	gain_fraction = 0.0;
+	init_screen();
+	init_lights();
+}
 
-	memset (current_screen, 0, sizeof (current_screen));
-	memset (pending_screen, 0, sizeof (pending_screen));
+void TranzportControlProtocol::invalidate_screen ()
+{
+ 	memset (current_screen, 0x7F, sizeof (current_screen)); // fill cache with a character we otherwise never use
+}
 
+void TranzportControlProtocol::init_screen ()
+{
+ 	memset (current_screen, 0x7F, sizeof (current_screen)); // fill cache with a character we otherwise never use
+ 	memset (pending_screen, ' ', sizeof (pending_screen)); // blank screen
+ 	memset (flash_screen, 0x7E, sizeof (pending_screen)); // fill cache with a character we otherwise never use
+}
+
+void TranzportControlProtocol::init_lights()
+{
 	for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) {
 		lights[i] = false;
 	}
@@ -100,6 +135,10 @@
 	for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) {
 		pending_lights[i] = false;
 	}
+
+	for (uint32_t i = 0; i < sizeof(flash_lights)/sizeof(flash_lights[0]); ++i) {
+		flash_lights[i] = false;
+	}
 }
 
 TranzportControlProtocol::~TranzportControlProtocol ()
@@ -149,9 +188,9 @@
 		} else {
 			cerr << "Begin tranzport shutdown\n";
 			pthread_cancel_one (thread);
-			cerr << "Thread dead\n";
-			// lcd_clear ();
-			// lights_off ();
+			cerr << "Tranzport Thread dead\n";
+			lcd_clear ();
+			lights_off ();
 			// cerr << "dev reset\n";
 			close ();
 			_active = false;
@@ -168,7 +207,7 @@
 	if (route_table[0]) {
 		gain_t g = route_get_gain (0);
 		if (g != last_track_gain) {
-			char buf[16];
+			char buf[9];
 			snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
 			print (0, 9, buf); 
 			last_track_gain = g;
@@ -208,6 +247,7 @@
 	lights_off ();
 	last_meter_fill = 0;
 	display_mode = DisplayBigMeter;
+	flush();
 }
 
 void
@@ -215,14 +255,14 @@
 {
 	last_where += 1; /* force time redisplay */
 	last_track_gain = FLT_MAX; /* force gain redisplay */
-
 	lcd_clear ();
 	lights_off ();
 	show_current_track ();
+	show_transport_time ();
+	show_track_gain ();
 	show_wheel_mode ();
-	show_wheel_mode ();
-	show_transport_time ();
 	display_mode = DisplayNormal;
+	flush();
 }
 
 
@@ -230,10 +270,11 @@
 log_meter (float db)
 {
 	float def = 0.0f; /* Meter deflection %age */
- 
-	if (db < -70.0f) {
-		def = 0.0f;
-	} else if (db < -60.0f) {
+
+	if (db < -70.0f) return 0.0f;
+ 	if (db > 6.0f) return 1.0f;
+
+	if (db < -60.0f) {
 		def = (db + 70.0f) * 0.25f;
 	} else if (db < -50.0f) {
 		def = (db + 60.0f) * 0.5f + 2.5f;
@@ -245,8 +286,6 @@
 		def = (db + 30.0f) * 2.0f + 30.0f;
 	} else if (db < 6.0f) {
 		def = (db + 20.0f) * 2.5f + 50.0f;
-	} else {
-		def = 115.0f;
 	}
 	
 	/* 115 is the deflection %age that would be 
@@ -267,6 +306,8 @@
 	float level = route_get_peak_input_power (0, 0);
 	float fraction = log_meter (level);
 
+	/* Someday add a peak bar*/
+
 	/* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
 	   the screen is 20 chars wide, so we can display 40 different levels. compute the level,
 	   then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
@@ -288,7 +329,7 @@
 	
 	if (fraction > 0.98) {
 		light_on (LightAnysolo);
-	}
+}
 
 	/* add all full steps */
 
@@ -318,10 +359,36 @@
 }
 
 void
+TranzportControlProtocol::show_bbt (nframes_t where)
+{ 
+	if (where != last_where) {
+	  char buf[16];
+	  BBT_Time bbt;
+	  session->tempo_map().bbt_time (where, bbt);
+	  sprintf (buf, "%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
+	  print (1, 9, buf);
+	  last_where = where;
+
+	  //        if (bbt_upper_info_label) {
+	  //     TempoMap::Metric m (session->tempo_map().metric_at (when));
+	  //     sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
+          //      bbt_lower_info_label->set_text (buf);
+          //      sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
+          //      bbt_upper_info_label->set_text (buf);
+        }
+}
+
+
+void
 TranzportControlProtocol::show_transport_time ()
 {
 	nframes_t where = session->transport_frame();
-	
+	show_bbt(where);
+}	
+
+void
+TranzportControlProtocol::show_smpte (nframes_t where)
+{
 	if (where != last_where) {
 
 		char buf[5];
@@ -343,7 +410,7 @@
 		print (1, 15, buf);
 
 		sprintf (buf, "%02" PRIu32, smpte.frames);
-		print (1, 18, buf);
+		print_noretry (1, 18, buf); 
 
 		last_where = where;
 	}
@@ -426,45 +493,233 @@
 }
 	
 int
-TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
 {
 	int val;
 
 	val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
 
-	if (val < 0)
+	if (val < 0) {
+#if DEBUG_TRANZPORT
+		printf("usb_interrupt_write failed: %d\n", val);
+#endif
 		return val;
-	if (val != 8)
+		}
+
+	if (val != 8) {
+#if DEBUG_TRANZPORT
+		printf("usb_interrupt_write failed: %d\n", val);
+#endif
 		return -1;
+		}
+
 	return 0;
 
 }	
 
-void
-TranzportControlProtocol::lcd_clear ()
+int
+TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
 {
-	/* special case this for speed and atomicity */
+#if MAX_RETRY > 0
+	int val;
+	int retry = 0;
+	while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
+		printf("usb_interrupt_write failed, retrying: %d\n", val);
+	}
 
-	uint8_t cmd[8];
+	if (retry == MAX_RETRY) {
+		printf("Too many retries on a tranzport write, aborting\n");
+		}
+
+	if (val < 0) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return val;
+		}
+	if (val != 8) {
+		printf("usb_interrupt_write failed: %d\n", val);
+		return -1;
+		}
+	return 0;
+#else
+	return (write_noretry(cmd,timeout_override));
+#endif
+
+}	
+
+
+// We have a state "Unknown" - STOP USING SPACES FOR IT - switching to arrow character
+// We have another state - no_retry. Misleading, as we still retry on the next pass
+// We have an "displayed" screen
+// We always draw into the pending screen
+// We have an active screen
+// Print arg - we have 
+// setactive
+// so someday I think we need a screen object.
+
+/*
+flash_screen.clear();
+flash_screen.print(0,0,"Undone:"); // Someday pull the undo stack from somewhere
+flash_screen.print(1,0,"Nextup:"); 
+
+if(flash_messages && lcd.getactive() != flash_screen) lcd.setactive(flash_screen,2000);
+
+screen::setactive(screen_name,duration); // duration in ms
+screen::getactive();
+*/
+
+
+int
+TranzportControlProtocol::flush ()
+{
+    return flush_lights() || flush_screen(); 
+}
+ 
+int
+TranzportControlProtocol::flush_lights ()
+{
+  int pending = 0;
+	/* flush changed light change */
+
+	if (pending_lights[LightRecord] != lights[LightRecord]) {
+		if (pending_lights[LightRecord]) {
+			light_on (LightRecord);
+		} else {
+			light_off (LightRecord);
+		}
+	}
+
+	if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
+		if (pending_lights[LightTracksolo]) {
+			light_on (LightTracksolo);
+		} else {
+			light_off (LightTracksolo);
+		}
+	}
+
+	if (pending_lights[LightTrackrec] != lights[LightTrackrec]) {
+		if (pending_lights[LightTrackrec]) {
+			light_on (LightTrackrec);
+		} else {
+			light_off (LightTrackrec);
+		}
+	}
+
+	if (pending_lights[LightTrackmute] != lights[LightTrackmute]) {
+		if (pending_lights[LightTrackmute]) {
+			light_on (LightTrackmute);
+		} else {
+			light_off (LightTrackmute);
+		}
+	}
+
+	if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
+		if (pending_lights[LightTracksolo]) {
+			light_on (LightTracksolo);
+		} else {
+			light_off (LightTracksolo);
+		}
+	}
+
+	if (pending_lights[LightAnysolo] != lights[LightAnysolo]) {
+		if (pending_lights[LightAnysolo]) {
+			light_on (LightAnysolo);
+		} else {
+			light_off (LightAnysolo);
+		}
+	}
+
+	if (pending_lights[LightLoop] != lights[LightLoop]) {
+		if (pending_lights[LightLoop]) {
+			light_on (LightLoop);
+		} else {
+			light_off (LightLoop);
+		}
+	}
+
+	if (pending_lights[LightPunch] != lights[LightPunch]) {
+		if (pending_lights[LightPunch]) {
+			light_on (LightPunch);
+		} else {
+			light_off (LightPunch);
+		}
+	}
+
+	return pending;
+}
+
+
+int
+TranzportControlProtocol::flush_screen ()
+{
+	/* next: flush LCD */
+  int cell, row, col_base, col, pending;
+  pending = 0;
+  cell = 0;
 	
-	cmd[0] = 0x00;
-	cmd[1] = 0x01;
-	cmd[3] = ' ';
-	cmd[4] = ' ';
-	cmd[5] = ' ';
-	cmd[6] = ' ';
-	cmd[7] = 0x00;
+	for (row = 0; row < 2 && pending == 0; ++row) {
+		for (col_base = 0, col = 0; col < 20 && pending == 0; ) {
+			if (pending_screen[row][col] != current_screen[row][col]) {
 
-	for (uint8_t i = 0; i < 10; ++i) {
-		cmd[2] = i;
-		usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 1000);
+				/* something in this cell is different, so dump the cell
+				   to the device.
+				*/
+
+				uint8_t cmd[8];
+				
+				cmd[0] = 0x00;
+				cmd[1] = 0x01;
+				cmd[2] = cell;
+				cmd[3] = pending_screen[row][col_base];
+				cmd[4] = pending_screen[row][col_base+1];
+				cmd[5] = pending_screen[row][col_base+2];
+				cmd[6] = pending_screen[row][col_base+3];
+				cmd[7] = 0x00;
+				if(write(cmd,DEFAULT_USB_TIMEOUT) != 0) {
+				  /* try to update this cell on the next go-round */
+#if DEBUG_TRANZPORT
+					printf("usb screen update failed for some reason... why? cmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); 
+#endif
+					pending += 1;
+				} else {
+					/* successful write: copy to current */
+				  memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
+				}
+
+				/* skip the rest of the 4 character cell since we wrote+copied it already */
+				
+				col_base += 4;
+				col = col_base;
+				cell++;
+
+			} else {
+
+				col++;
+				
+				if (col && col % 4 == 0) {
+					cell++;
+					col_base += 4;
+				}
+			}
+		}
 	}
-	
-	memset (current_screen, ' ', sizeof (current_screen));
-	memset (pending_screen, ' ', sizeof (pending_screen));
+
+
+  return pending;
 }
 
+
 void
+TranzportControlProtocol::lcd_clear ()
+{
+	const char *blank = "                    ";
+	invalidate_screen();
+	print(0,0,blank); // and rewrite the pending_screen
+	print(1,0,blank);
+}
+
+// Next up - buffering the lights more appropriately
+
+void
 TranzportControlProtocol::lights_off ()
 {
 	uint8_t cmd[8];
@@ -478,31 +733,31 @@
 	cmd[7] = 0x00;
 
 	cmd[2] = LightRecord;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightRecord] = false;
 	}
 	cmd[2] = LightTrackrec;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackrec] = false;
 	}
 	cmd[2] = LightTrackmute;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTrackmute] = false;
 	}
 	cmd[2] = LightTracksolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightTracksolo] = false;
 	}
 	cmd[2] = LightAnysolo;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightAnysolo] = false;
 	}
 	cmd[2] = LightLoop;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightLoop] = false;
 	}
 	cmd[2] = LightPunch;
-	if (write (cmd, 1000) == 0) {
+	if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 		lights[LightPunch] = false;
 	}
 }
@@ -523,7 +778,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = true;
 			return 0;
 		} else {
@@ -551,7 +806,7 @@
 		cmd[6] = 0x00;
 		cmd[7] = 0x00;
 
-		if (write (cmd, 1000) == 0) {
+		if (write (cmd, DEFAULT_USB_TIMEOUT) == 0) {
 			lights[light] = false;
 			return 0;
 		} else {
@@ -571,11 +826,13 @@
 	uint8_t buf[8];
 	int val;
 	bool first_time = true;
+	int pending = 0;
+	uint8_t offline = 0;
 
 	PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
 
 	memset (&rtparam, 0, sizeof (rtparam));
-	rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */
+	rtparam.sched_priority = 52; /* XXX should be relative to audio (JACK) thread */
 	
 	if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
 		// do we care? not particularly.
@@ -593,13 +850,17 @@
 
 		/* anything to read ? */
 
-		if (_device_status == STATUS_OFFLINE) {
-			light_off (LightRecord);
-			first_time = true;
-		}
+	  if (_device_status == STATUS_OFFLINE) {
+	    if(offline++ == 1) { 
+	      cerr << "Transport has gone offline\n";
+	    }
+	    first_time = true;
+	  } else { 
+	    offline = 0; 
+	  }
 
 		pthread_testcancel();
-		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10);
+		val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 20);
 		pthread_testcancel();
 
 		if (val == 8) {
@@ -611,204 +872,96 @@
 				lcd_clear ();
 				lights_off ();
 				first_time = false;
+				offline = 0;
 			}
 			/* update whatever needs updating */
-			update_state ();
-		}
+			pending = update_state ();
+		} 
 	}
 
 	return (void*) 0;
 }
 
+
+int TranzportControlProtocol::set_lights_normal() 
+{
+  if (route_table[0]) {
+    boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]);
+    if (at && at->record_enabled()) {
+      pending_lights[LightTrackrec] = true;
+    } else {
+      pending_lights[LightTrackrec] = false;
+    }
+    if (route_get_muted (0)) {
+      pending_lights[LightTrackmute] = true;
+    } else {
+      pending_lights[LightTrackmute] = false;
+    }
+    if (route_get_soloed (0)) {
+      pending_lights[LightTracksolo] = true;
+    } else {
+      pending_lights[LightTracksolo] = false;
+    }
+    
+  } else {
+    pending_lights[LightTrackrec] = false;
+    pending_lights[LightTracksolo] = false;
+    pending_lights[LightTrackmute] = false;
+  }
+  
+  /* global */
+  
+  if (session->get_play_loop()) {
+    pending_lights[LightLoop] = true;
+  } else {
+    pending_lights[LightLoop] = false;
+  }
+  
+  if (Config->get_punch_in() || Config->get_punch_out()) {
+    pending_lights[LightPunch] = true;
+  } else {
+    pending_lights[LightPunch] = false;
+  }
+  
+  if (session->get_record_enabled()) {
+    pending_lights[LightRecord] = true;
+  } else {
+    pending_lights[LightRecord] = false;
+  }
+  
+  if (session->soloing ()) {
+    pending_lights[LightAnysolo] = true;
+  } else {
+    pending_lights[LightAnysolo] = false;
+  }
+  
+  return 0;
+}
+
+int TranzportControlProtocol::set_lights_tempo() 
+{
+  return set_lights_normal();
+  // someday soon fiddle with the lights based on the tempo 
+}
+
 int
 TranzportControlProtocol::update_state ()
 {
-	int row;
-	int col_base;
-	int col;
-	int cell;
+	/* do the text and light updates */
 
-	/* do the text updates */
-
 	switch (display_mode) {
 	case DisplayBigMeter:
-		show_meter ();
-		break;
+	  set_lights_tempo();
+	  show_meter ();
+	  break;
 
 	case DisplayNormal:
-		normal_update ();
-		break;
+	  set_lights_normal();
+	  normal_update ();
+	  break;
 	}
 
-	/* next: flush LCD */
-
-	cell = 0;
-	
-	for (row = 0; row < 2; ++row) {
-
-		for (col_base = 0, col = 0; col < 20; ) {
-			
-			if (pending_screen[row][col] != current_screen[row][col]) {
-
-				/* something in this cell is different, so dump the cell
-				   to the device.
-				*/
-
-				uint8_t cmd[8];
-				
-				cmd[0] = 0x00;
-				cmd[1] = 0x01;
-				cmd[2] = cell;
-				cmd[3] = pending_screen[row][col_base];
-				cmd[4] = pending_screen[row][col_base+1];
-				cmd[5] = pending_screen[row][col_base+2];
-				cmd[6] = pending_screen[row][col_base+3];
-				cmd[7] = 0x00;
-
-				if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 1000) == 8) {
-					/* successful write: copy to current */
-					memcpy (&current_screen[row][col_base], &pending_screen[row][col_base], 4);
-				}
-
-				/* skip the rest of the 4 character cell since we wrote+copied it already */
-				
-				col_base += 4;
-				col = col_base;
-				cell++;
-
-			} else {
-
-				col++;
-				
-				if (col && col % 4 == 0) {
-					cell++;
-					col_base += 4;
-				}
-			}
-		}
-	}
-
-	/* now update LED's */
-
-	/* per track */
-
-	if (route_table[0]) {
-		boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]);
-		if (at && at->record_enabled()) {
-			pending_lights[LightTrackrec] = true;
-		} else {
-			pending_lights[LightTrackrec] = false;
-		}
-		if (route_get_muted (0)) {
-			pending_lights[LightTrackmute] = true;
-		} else {
-			pending_lights[LightTrackmute] = false;
-		}
-		if (route_get_soloed (0)) {
-			pending_lights[LightTracksolo] = true;
-		} else {
-			pending_lights[LightTracksolo] = false;
-		}
-
-	} else {
-		pending_lights[LightTrackrec] = false;
-		pending_lights[LightTracksolo] = false;
-		pending_lights[LightTrackmute] = false;
-	}
-
-	/* global */
-
-	if (session->get_play_loop()) {
-		pending_lights[LightLoop] = true;
-	} else {
-		pending_lights[LightLoop] = false;
-	}
-
-	if (Config->get_punch_in() || Config->get_punch_out()) {
-		pending_lights[LightPunch] = true;
-	} else {
-		pending_lights[LightPunch] = false;
-	}
-
-	if (session->get_record_enabled()) {
-		pending_lights[LightRecord] = true;
-	} else {
-		pending_lights[LightRecord] = false;
-	}
-
-	if (session->soloing ()) {
-		pending_lights[LightAnysolo] = true;
-	} else {
-		pending_lights[LightAnysolo] = false;
-	}
-
-	/* flush changed light change */
-
-	if (pending_lights[LightRecord] != lights[LightRecord]) {
-		if (pending_lights[LightRecord]) {
-			light_on (LightRecord);
-		} else {
-			light_off (LightRecord);
-		}
-	}
-
-	if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
-		if (pending_lights[LightTracksolo]) {
-			light_on (LightTracksolo);
-		} else {
-			light_off (LightTracksolo);
-		}
-	}
-
-	if (pending_lights[LightTrackrec] != lights[LightTrackrec]) {
-		if (pending_lights[LightTrackrec]) {
-			light_on (LightTrackrec);
-		} else {
-			light_off (LightTrackrec);
-		}
-	}
-
-	if (pending_lights[LightTrackmute] != lights[LightTrackmute]) {
-		if (pending_lights[LightTrackmute]) {
-			light_on (LightTrackmute);
-		} else {
-			light_off (LightTrackmute);
-		}
-	}
-
-	if (pending_lights[LightTracksolo] != lights[LightTracksolo]) {
-		if (pending_lights[LightTracksolo]) {
-			light_on (LightTracksolo);
-		} else {
-			light_off (LightTracksolo);
-		}
-	}
-
-	if (pending_lights[LightAnysolo] != lights[LightAnysolo]) {
-		if (pending_lights[LightAnysolo]) {
-			light_on (LightAnysolo);
-		} else {
-			light_off (LightAnysolo);
-		}
-	}
-
-	if (pending_lights[LightLoop] != lights[LightLoop]) {
-		if (pending_lights[LightLoop]) {
-			light_on (LightLoop);
-		} else {
-			light_off (LightLoop);
-		}
-	}
-
-	if (pending_lights[LightPunch] != lights[LightPunch]) {
-		if (pending_lights[LightPunch]) {
-			light_on (LightPunch);
-		} else {
-			light_off (LightPunch);
-		}
-	}
-
-	return 0;
+	return flush();
 }
 
 int
@@ -981,11 +1134,20 @@
 void
 TranzportControlProtocol::show_current_track ()
 {
-	if (route_table[0] == 0) {
-		print (0, 0, "--------");
-	} else {
-		print (0, 0, route_get_name (0).substr (0, 8).c_str());
-	}
+  char pad[11];
+  char *v;
+  int len;
+  if (route_table[0] == 0) {
+    print (0, 0, "----------");
+    last_track_gain = FLT_MAX;
+  } else {
+    strcpy(pad,"          ");
+    v =  (char *)route_get_name (0).substr (0, 10).c_str();
+    if((len = strlen(v)) > 0) {
+      strncpy(pad,(char *)v,len);
+    }
+    print (0, 0, pad);
+  }
 }
 
 void
@@ -1006,6 +1168,12 @@
 void
 TranzportControlProtocol::button_event_backlight_release (bool shifted)
 {
+	if (shifted) {
+		lcd_clear();
+		last_where += 1; /* force time redisplay */
+		last_track_gain = FLT_MAX;
+		normal_update(); //  redraw_screen();  
+	}
 }
 
 void
@@ -1080,9 +1248,9 @@
 TranzportControlProtocol::button_event_undo_press (bool shifted)
 {
 	if (shifted) {
-		redo ();
+	  redo (); // someday flash the screen with what was redone
 	} else {
-		undo ();
+	  undo (); // someday flash the screen with what was undone
 	}
 }
 
@@ -1235,7 +1403,11 @@
 void
 TranzportControlProtocol::button_event_play_press (bool shifted)
 {
-	transport_play ();
+	if (shifted) {
+	  set_transport_speed (1.0f);
+	} else {
+	  transport_play ();
+	}
 }
 
 void
@@ -1304,8 +1476,11 @@
 				}
 				break;
 
+			case WheelShiftMarker:
+			        break;
 			case WheelShiftMaster:
 				break;
+
 			}
 		}
 
@@ -1453,6 +1628,8 @@
 		break;
 	case WheelShiftMaster:
 		wheel_shift_mode = WheelShiftGain;
+	case WheelShiftMarker: // Not done yet, disabled
+	  wheel_shift_mode = WheelShiftGain;
 	}
 
 	show_wheel_mode ();
@@ -1512,19 +1689,31 @@
 		break;
 
 	case WheelShiftPan:
-		text += ":Pan";
+		text += ":Pan ";
 		break;
 
 	case WheelShiftMaster:
 		text += ":Mstr";
 		break;
+
+	case WheelShiftMarker:
+   	        text += ":Mrkr";
+		break;
 	}
 	
 	print (1, 0, text.c_str());
 }
 
+// Was going to keep state around saying to retry or not
+// haven't got to it yet.
+
+	void
+TranzportControlProtocol::print (int row, int col, const char *text) {
+	print_noretry(row,col,text);
+}
+
 void
-TranzportControlProtocol::print (int row, int col, const char *text)
+TranzportControlProtocol::print_noretry (int row, int col, const char *text)
 {
 	int cell;
 	uint32_t left = strlen (text);
Index: libs/surfaces/tranzport/tranzport_control_protocol.h
===================================================================
--- libs/surfaces/tranzport/tranzport_control_protocol.h	(revision 1247)
+++ libs/surfaces/tranzport/tranzport_control_protocol.h	(working copy)
@@ -1,3 +1,4 @@
+
 #ifndef ardour_tranzport_control_protocol_h
 #define ardour_tranzport_control_protocol_h
 
@@ -72,7 +73,8 @@
 	enum WheelShiftMode {
 		WheelShiftGain,
 		WheelShiftPan,
-		WheelShiftMaster
+		WheelShiftMaster,
+		WheelShiftMarker
 	};
 		
 	enum WheelMode {
@@ -102,8 +104,10 @@
 	Glib::Mutex update_lock;
 	char current_screen[2][20];
 	char pending_screen[2][20];
+	char flash_screen[2][20];
 	bool lights[7];
 	bool pending_lights[7];
+	bool flash_lights[7];
 
 	bool           last_negative;
 	uint32_t       last_hrs;
@@ -121,12 +125,14 @@
 	int open ();
 	int read (uint32_t timeout_override = 0);
 	int write (uint8_t* cmd, uint32_t timeout_override = 0);
+	int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
 	int close ();
 
 	int open_core (struct usb_device*);
 
 	void lcd_clear ();
 	void print (int row, int col, const char* text);
+	void print_noretry (int row, int col, const char* text);
 	int  light_on (LightID);
 	int  light_off (LightID);
 	void lights_off ();
@@ -141,6 +147,8 @@
 	void show_current_track ();
 	void show_track_gain ();
 	void show_transport_time ();
+	void show_bbt (nframes_t where);
+	void show_smpte (nframes_t where);
 	void show_wheel_mode ();
 	void show_gain ();
 	void show_pan ();
@@ -208,6 +216,14 @@
 
 	int process (uint8_t *);
 	int update_state();
+	int flush();
+	int flush_lights();
+	int flush_screen();
+	void init_lights();
+	int set_lights_normal();
+	int set_lights_tempo();
+	void init_screen();
+	void invalidate_screen();
 };
 
 
tranzport_04.patch (24,808 bytes)

mtaht

2006-12-27 07:48

developer   ~0002954

tranzport_04 patch uploaded. I've been really happy with this one so far. When the tranzport drops offline, it comes right back up without any silliness, and the screen display is largely perfect... so a few more testers would be nice.

the next-track() bug is bugging me....

2006-12-28 05:50

 

tranzport_driver_06.broken.stack.scribbled.on.somewhere.by.somebody.not.me.tgz (12,798 bytes)

mtaht

2006-12-28 05:56

developer   ~0002956

Last edited: 2006-12-28 06:27

The tranzport06 driver I just uploaded is broken badly. Don't try it.

It's worthwhile to look at for the enhancements to the API, and if you love debugging threads in real time processes, it's something you're gonna LOVE.

I spent the day revising and cleaning up the API to the tranzport driver and slowly realizing that I needed to abstract out lcd, screen, buttons, and lights into their own classes.

I did put in a bunch of new functionality (my fav - enable_bling_mode()) and start getting to where I was caching writes to everything.

However, that upload and core dump had a stupid unitialized variable in it that essej2 spotted right off.

There will hopefully be a more complete driver uploaded now that I'm past this.

2006-12-28 05:59

 

core.16198.gz (3,908,673 bytes)

mtaht

2006-12-28 06:10

developer   ~0002957

Last edited: 2006-12-28 18:47

sampo_v2 found that the tranzport04 code would fail when you turned on remote control ids in the gui - on a multi-processor core duo in x86 mode. Doesn't happen for me on a single processor x86_64, does happen on a dual.

2006-12-28 08:42

 

tranzport_driver_07.tgz (12,988 bytes)

mtaht

2006-12-28 08:44

developer   ~0002958

Last edited: 2006-12-28 19:24

uploaded tranzport07, which is functionally equivalent and should work as well as the tranzport04 driver. The patch is now 4x bigger than the original, so this code just drops in on top of current svn.

Some major cleanups in the API, full caching of lights, stubs for a great deal of new functionality added..

I have some other API changes in mind shortly. I don't like separate functions for press and release. I think I can catch more button presses if I have a single function for both. Still, threaded reader/writer might help...

And I think I can quash the last of the screen redisplay bugs in the next version, and if I get into a single function for button events might be able to improve matters there. It's not the truly right answer, but reliability is my god. I'm growing just as prepared as paul to get rid of libusb one way or another.

2006-12-29 00:46

 

tranzport_driver_09.tgz (12,761 bytes)

mtaht

2006-12-29 00:47

developer   ~0002960

some more cleanups. Am hoping to knock out the last of the screen update issues in the next cut.

2006-12-29 07:15

 

tranzport_10.tgz (13,562 bytes)

mtaht

2006-12-29 07:25

developer   ~0002961

I have killed the last of the redisplay bugs. It's a little more brute force than I'd like, but it'll get cleaner later.

Button response is fast and accurate on this version.

It remains possible to crash the tranzport for a few seconds, especially when switching modes or joggling the shuttle madly.

Tuning the driver may well be hardware specific. You can fiddle with the MAX_TRANZPORT_INFLIGHT parameter and the pending = flush() processing to move things around some.

Not tested on a dual processor yet.

I'd be interested in cpu usage information with this vs the old driver, especially when ardour is idle. Also any seizures of ardour itself while using it. I've managed to crash the gtk_ardour gui (while ardour kept running!) at least once.

2006-12-29 10:57

 

tranzport_driver_11.tgz (13,731 bytes)

mtaht

2006-12-29 10:57

developer   ~0002962

tranzport driver .11 - Now with blinkenlights for the beat.

mtaht

2006-12-29 21:03

developer   ~0002963

Sampo has committed version .11 to svn trunk, revision 1252. It seems to work well, under normal use, on dual core x86, x86_64 and dual opteron. Anybody got a mac?

It remains possible to disconnect the interface via jiggling the scroll wheel really fast or hitting shift-backlight really fast. You can permanently disconnect it (and it requires 4 tries to restart) by hitting track forward/backward.

I note - this is hitting these buttons "REALLY, REALLY FAST". under normal use you shouldn't see these issues.

Minor nits:
Still occasionally have trouble redrawing the display - shift-backlight will fix it.
If tracks are soloed, on load, the any solo light doesn't light

I'd do some more work on the init/close and track issues but I need to do some extensive "testing" - e.g. music making - now!

Enjoy.

mtaht

2006-12-30 11:58

developer   ~0002965

tranzport_12 is basically work in progress. 11 is more functional, stick with that unless you love the bleeding edge.

Conversion to C++ bitsets. Cuts 35 bytes off the Tranzport object and speeds up a few things. A 40 character LCD is the first use for a 64 bit bitwise operation I've yet had.

New feature - ***stereo*** mini meter - currently hard coded to run all the time, will one day just run when the transport is moving.

Have absolute proof that the screen caching routine is not doing the right thing, I shouldn't have to do a "lcd_damage" in the mini-meter code. It's a simple logic inversion somewhere but I can't find it at the moment.

2006-12-30 11:59

 

tranzport_12.tgz (14,269 bytes)

2006-12-31 01:22

 

tranzport_14.tgz (14,432 bytes)

mtaht

2006-12-31 01:26

developer   ~0002968

Last edited: 2006-12-31 01:29

Announcing the tranzport .14 driver (.13 skipped for religious reasons).

A complete conversion to C++ bitsets hopefully leads to a faster version. It certainly lent clarity to the code. And I think I have killed off the last of the display glitches. (but I've thought that before)

There's also a 9 bar stereo meter for playback now. It looks great! The big screen meter looks less great, the flush routine needs to update columnwise rather than rowwise - but i will make that a true stereo meter shortly.

And... we have footswitch support now!

Revising the wheel code is next. It's the biggest source of crashes and I want to throw out the gettimeofday stuff entirely.

mtaht

2007-01-02 11:49

developer   ~0002969

Last edited: 2007-01-02 12:10

I spent a fruitless day looking for any differences in the input state machine that would help. Notes:

1) There are actually 3 states - OK (0), Online (1), and Offline(ff). The differences between the OK and Online states are subtle and perhaps important. I collected reams of data on it but have been unable to discern a pattern.

2) a usb failure is a usb failure, that's all. You can't determine a lot about it from the libusb layer. Whether or not it's possible to make libusb do the right thing remains to be seen. I would like to revise the timer code to be a global timer (or slave to a global timer) and let that actually drive other events like the reader/writer threads... but I mostly think a kernel driver would be more elegant at this point. Perhaps a hybrid driver to start, reads (including timer wheel events) could come from a kernel driver, and commands go through libusb....

3) My blinkenlights aren't fast enough.At 60bpm they are ok for tempo, but at 100+ they get weird.

I really don't know how to make it less flaky without a major rewrite of the I/O and I'm going to concentrate on making what I have reliable "enough", so long as you go easy on the scroll wheel, and migrate to other problems, like managing screens and rolling a kernel driver.

---


I'm putting this here because I don't really have a place to put it, unless I create a web page and have a place to keep the code.

While doing some exaustive testing of my latest code (read - playing a ton of music) I have done some thinking about the gui, and decided that under apparent simplicity should lie complexity.

The "flash" screen idea I am going to drop, and replace it with the idea of a notify area being declared on each screen.

When the unit is idle, these messages will appear in that area statically. When the transport is running, these messages will still appear in that space, which is usually where the meter is. It's actually possible to rapidly flash the area between the competing writers and get a nifty faded effect. (I came across this idea accidentally when I had a pointer overrun)

I need a way to get messages back into the tranzport. Example - I hit Undo, what was undone? Redo, same problem.

I've already found many uses for being able to control more than 1 track at a time, so I think that although I'm *usually* controlling one track at a time, being able to quickly access all tracks would be good.

Example - want to have meters for all tracks running and be able to control the
db/pan settings of the track I'm on....

What I am going to go with is multifold - but first my design goal: I want to do everything required for a solo musician wielding an instrument to NOT have to touch a keyboard or look at a big screen. When you are wrapped behind a bass, it's difficult to cope with that - but the tranzport is a great alternative.

Most screens will have *4* items on them.

There will be "display views" - which are more informational and bling oriented.

There will be "Track based views"- which basically do track specific things

There will be "interactive views",which basically allow for more input than output. They will do something to highlight the current selection.
Scroll wheel will select between values for a field. Track Next/Prev will move between fields. I would like to have a "No/Yes" option (hit record for yes? Stop for no?) but I'm still a little vague on that.

Things to do:

A) Hitting "Shift->Spacebar" will switch "views". There are ultimately going to be dozens of views. At present, there are only the "Normal" and "Big Meter" views.

Each view will change somewhat based on the state of the transport. Holding down shift for a second will switch to the "underlying display"....

Here's how the "normal" view looks today in my tree:

VIEW: NORMAL

Play Mode: Stopped
[Trackname[16]] [gain/pan[4]]
[Modes[9]] long smpte/bar counter]

Play Mode: Playing > 1.0 speed
[Trackname[16]] [gain/pan[4]]
[meter[16]] short smpte/bar counter]

Play Mode: Playing < 1.0
[Trackname[16]] [gain/pan[4]]
[meter[9]] long smpte/bar counter]

Play Mode: Recording
[Trackname[16]] [gain/pan[4]]
[meter[16]] short smpte/bar counter]

Other views (in order of development priority)

Marker Mode: Edit markers, setup loops and punch in points.
Config Mode: Load/Save settings, Load/Save project. Set wheel SnapTo
Loop Mode: Show track, raise layers to top for playback, editing, deletion, loop on and off, etc

It's possible that config mode will have a "MORE" field, or ways to move around the configuration (ffw/Play?)

(the first two are the two modes I most need personally. If you have a suggestion...)

Mastering Mode - display master and current track with meters and panner/db
Automation Mode - I really don't think I have the pixels for this

(I've already abstracted out the code to do most of these, but it's bling, I'm not going to bother much with it soon)

Quad Meter Mode
Inverted Meter mode (draws meters backwards)
Quad Inverted Meter mode (bling, but my car stereo has it, and it's cool)
10 8 bar meter mode
5 8 bar meter mode

I haven't written the panner yet, doing the stereo meter killed me.

From a development perspective I'm going to keep revising the code to make it more stable and merely tie the new mode modes to the "bling mode"s until they are ready for prime time. I should be able to put out releases once a week for a while.

A big help would be moving these items into a higher level of abstraction (revising the baseUI class).

In particular, I'd really like "slave" mode. Snapto increment is really important....

Here's an example of something that should be fairly easy to export to the Base::UI subclasses - the current state of the main keyboard's shift key.

That way, when shift is held down for a few seconds on the regular keyboard, I can see what's underneath the current tranzport display mode (I do like big meters) (Also, it's somewhat easier to hit shift on the main keyboard and play on the tranzport or the shuttle wheel, if that's what you are doing).

Should be fairly easy to tap into the gdk event for this but the "right way" to propagate this event into the class is beyond me.

mtaht

2007-01-04 11:58

developer   ~0002992

Some good email went by today which included info on implementing the panner for the mackie which will apply to this.

mtaht

2007-01-04 12:18

developer   ~0002993

1) I plan to do one more release of the libusb based code, which I've taken about as far as it can go. Then some documentation of the current UI.

After that I'm giving up and moving towards a kernel driver. That's going to take some time. Also, I see from the mackie based work that there's some commonality between interface goals and perhaps some effort can be expended up there in userspace on some universal widgets for lcds and buttons.

I was really enjoying my foray into C++, but I suppose I gotta do the kernel driver...

2) Tranzport kernel driver status:

First the good news - what I have presently A) doesn't crash the tranzport no matter how much data I write or how fast I spin the control wheel. This is a vast improvement over the userspace driver. (Also, draining the device 8 bytes at a time from userspace doesn't help either)

B) has perfectly working write support. I can flood the tranzport with data and still not knock it offline.

C) The driver actually worked the first time after about 3 weeks of thought and 3 hours of coding. I've used up all my luck for the year.

The bad news.

A) the existing write support, while nice, needs a smarter buffer if it's going to update the lights in tempo. As there are only 17 commands on the tranzport, it's straightforward to look for an urb in flight and cancel/reschedule it. I'd like to aim for a maximum response time of about 40ms, maybe as little as 20ms.

B) Read support is currently busted. I'm a lot closer than I was - I got rid of the random data overruns - after I rewrote the ringbuffer code to be more clear - but I'm still getting duplicate events from the ringbuffer/interrupt handler interaction, and I don't know why. I'd really like to lick this problem as I should then be able to get to where I have something that will be as good as userspace, before tackling C)...

C) I stayed away from the shuttle wheel code in userspace on purpose. It's hairy to handle there, and hairy in kernel space as well. Not only do you care about having it move accurately, but you also respect the speed, duration and total distance of the move, AND what keys were pressed during the move. AND, at least based on my experiments so far, something somewhere has to be compressing all that data together so that your main app can do something intelligent with the information when it gets around to it.

So I have 2 paths:

path A) is to get something that just lets me dump commands to the device. I've been 20 minutes away from that point for the last 16 hours.

path B) is to roll a real API for the thing, which does event compression and has a slightly saner treatment of button events. I am thinking of implementing a software cursor at one layer or another.

mtaht

2007-01-08 20:48

developer   ~0003031

There is now an improved version of the libusb userspace driver in the frontierdesigns branch in svn.

Improvements:

mini-meters while playing or recording
tempo lights when tempo is reasonable
variable sized bar/beat display
notification of what track you're on in big meter mode
notification of what marker you are on with next_marker/prev_marker
support for the footswitch.

Tons of mostly useless debugging code
Preliminary and disabled threaded code

Issue History

Date Modified Username Field Change
2006-12-02 04:03 mtaht New Issue
2006-12-02 04:15 mtaht Note Added: 0002837
2006-12-02 04:32 mtaht Note Added: 0002839
2006-12-02 05:28 mtaht Note Added: 0002840
2006-12-02 22:19 mtaht Note Added: 0002845
2006-12-02 22:55 mtaht Note Added: 0002846
2006-12-02 23:39 mtaht Note Added: 0002847
2006-12-03 01:15 mtaht Note Added: 0002848
2006-12-03 01:16 mtaht File Added: debug_tranzport.patch
2006-12-03 01:29 mtaht Note Added: 0002849
2006-12-03 06:17 mtaht Note Added: 0002850
2006-12-03 16:31 mtaht Note Added: 0002852
2006-12-03 17:35 mtaht Note Edited: 0002852
2006-12-05 16:27 paul Note Added: 0002865
2006-12-05 17:39 mtaht Note Added: 0002869
2006-12-05 17:42 mtaht Note Added: 0002870
2006-12-26 04:49 mtaht Note Added: 0002951
2006-12-26 05:05 mtaht File Added: transport_02.patch
2006-12-26 18:46 mtaht File Added: tranzport03.patch
2006-12-26 18:56 mtaht Note Added: 0002952
2006-12-26 19:25 mtaht Note Added: 0002953
2006-12-27 07:22 mtaht Note Edited: 0002953
2006-12-27 07:25 mtaht File Added: tranzport_04.patch
2006-12-27 07:38 mtaht Note Edited: 0002951
2006-12-27 07:48 mtaht Note Added: 0002954
2006-12-28 05:50 mtaht File Added: tranzport_driver_06.broken.stack.scribbled.on.somewhere.by.somebody.not.me.tgz
2006-12-28 05:56 mtaht Note Added: 0002956
2006-12-28 05:59 mtaht File Added: core.16198.gz
2006-12-28 06:10 mtaht Note Added: 0002957
2006-12-28 06:27 mtaht Note Edited: 0002956
2006-12-28 08:42 mtaht File Added: tranzport_driver_07.tgz
2006-12-28 08:44 mtaht Note Added: 0002958
2006-12-28 18:47 mtaht Note Edited: 0002957
2006-12-28 19:24 mtaht Note Edited: 0002958
2006-12-29 00:46 mtaht File Added: tranzport_driver_09.tgz
2006-12-29 00:47 mtaht Note Added: 0002960
2006-12-29 07:15 mtaht File Added: tranzport_10.tgz
2006-12-29 07:25 mtaht Note Added: 0002961
2006-12-29 10:57 mtaht File Added: tranzport_driver_11.tgz
2006-12-29 10:57 mtaht Note Added: 0002962
2006-12-29 21:03 mtaht Note Added: 0002963
2006-12-30 11:58 mtaht Note Added: 0002965
2006-12-30 11:59 mtaht File Added: tranzport_12.tgz
2006-12-31 01:22 mtaht File Added: tranzport_14.tgz
2006-12-31 01:26 mtaht Note Added: 0002968
2006-12-31 01:29 mtaht Note Edited: 0002968
2007-01-02 11:49 mtaht Note Added: 0002969
2007-01-02 11:50 mtaht Note Edited: 0002969
2007-01-02 12:10 mtaht Note Edited: 0002969
2007-01-04 11:58 mtaht Note Added: 0002992
2007-01-04 12:18 mtaht Note Added: 0002993
2007-01-08 20:48 mtaht Note Added: 0003031
2007-01-16 23:26 mtaht Relationship added has duplicate 0001344