$OpenBSD: patch-src_lib_drivers_JackDriver_cpp,v 1.1 2008/03/24 03:09:42 jakemsr Exp $
--- src/lib/drivers/JackDriver.cpp.orig	Fri Feb 29 17:46:02 2008
+++ src/lib/drivers/JackDriver.cpp	Sat Mar  1 00:21:04 2008
@@ -59,8 +59,10 @@ JackDriver::JackDriver(JackProcessCallback processCall
 {
 	infoLog( "INIT" );
 	jackDriverInstance = this;
-	track_out_flag = true;
 	this->processCallback = processCallback;
+
+	must_relocate = 0;
+	bbt_frame_offset = 0;
 }
 
 
@@ -94,14 +96,25 @@ int JackDriver::connect()
 
 	if ( connect_out_flag ) {
 	// connect the ports
-		if ( jack_connect ( client, jack_port_name( output_port_1 ), output_port_name_1.c_str() ) ) {
-			( Hydrogen::getInstance() )->raiseError( Hydrogen::JACK_CANNOT_CONNECT_OUTPUT_PORT );
+		if ( jack_connect ( client, jack_port_name( output_port_1 ), output_port_name_1.c_str() ) == 0 &&
+			jack_connect ( client, jack_port_name( output_port_2 ), output_port_name_2.c_str() ) == 0 ) {
+			return 0;
+		}
+
+		infoLog("Could not connect to saved out-ports. Connecting to first pair of in-ports");
+		const char ** portnames = jack_get_ports ( client, NULL, NULL, JackPortIsInput );
+		if ( !portnames || !portnames[0] || !portnames[1] ) {
+			errorLog( "Couldn't locate two Jack input ports" );
+			Hydrogen::getInstance()->raiseError( Hydrogen::JACK_CANNOT_CONNECT_OUTPUT_PORT );
 			return 2;
 		}
-		if ( jack_connect ( client, jack_port_name( output_port_2 ), output_port_name_2.c_str() ) ) {
-			( Hydrogen::getInstance() )->raiseError( Hydrogen::JACK_CANNOT_CONNECT_OUTPUT_PORT );
+		if ( jack_connect ( client, jack_port_name( output_port_1 ), portnames[0] ) != 0 ||
+			jack_connect ( client, jack_port_name( output_port_2 ), portnames[1] ) != 0 ) {
+			errorLog( "Couldn't connect to first pair of Jack input ports" );
+			Hydrogen::getInstance()->raiseError( Hydrogen::JACK_CANNOT_CONNECT_OUTPUT_PORT );
 			return 2;
 		}
+		free( portnames );
 	}
 
 	return 0;
@@ -159,9 +172,61 @@ unsigned JackDriver::getSampleRate()
 	return jack_server_sampleRate;
 }
 
+void JackDriver::calculateFrameOffset()
+{
+	bbt_frame_offset = m_JackTransportPos.frame - m_transport.m_nFrames;
+}
 
 
 
+/// Take beat-bar-tick info from the Jack system, and translate it to a new internal frame position and ticksize.
+void JackDriver::relocateBBT()
+{
+	if ( m_transport.m_status != TransportInfo::ROLLING || !( m_JackTransportPos.valid & JackPositionBBT ) /**the last check is *probably* redundant*/ ) return;
+
+	Hydrogen * H = Hydrogen::getInstance();
+	Song * S = H->getSong();
+
+	float hydrogen_TPB = ( float )S->m_nResolution;
+
+	long bar_ticks = 0;
+	if ( S->getMode() == Song::SONG_MODE ) {
+		bar_ticks = H->getTickForPosition( m_JackTransportPos.bar ); // (Reasonable?) assumption that one pattern is _always_ 1 bar long!
+		if ( bar_ticks < 0 ) bar_ticks = 0; // ignore error NOTE This is wrong -- if loop state is off, transport should just stop ??
+	}
+	float hydrogen_ticks_to_locate =  bar_ticks + ( m_JackTransportPos.beat-1 )*hydrogen_TPB + m_JackTransportPos.tick*( hydrogen_TPB/m_JackTransportPos.ticks_per_beat );
+
+	// char bbt[15];
+	// sprintf( bbt, "[%d,%d,%d]", m_JackTransportPos.bar, m_JackTransportPos.beat, m_JackTransportPos.tick );
+	string bbt;
+	bbt = "[" + toString(m_JackTransportPos.bar) +
+		"," + toString(m_JackTransportPos.beat) +
+		"," + toString(m_JackTransportPos.tick) + "]";
+	warningLog( "Locating BBT: " + bbt + /*" -- Tx/Beat = "+toString(m_JackTransportPos.ticks_per_beat)+", Meter "+toString(m_JackTransportPos.beats_per_bar)+"/"+toString(m_JackTransportPos.beat_type)+*/" =>tick " + toString( hydrogen_ticks_to_locate ) );
+
+	float fNewTickSize = getSampleRate() * 60.0 /  m_transport.m_nBPM / S->m_nResolution;;
+	// not S->m_fBPM !??
+
+	if ( fNewTickSize == 0 ) return; // ??!?
+
+	// NOTE this _should_ prevent audioEngine_process_checkBPMChanged in Hydrogen.cpp from recalculating things.
+	m_transport.m_nTickSize = fNewTickSize;
+
+	long long nNewFrames = ( long long )( hydrogen_ticks_to_locate * fNewTickSize );
+	if ( m_JackTransportPos.valid & JackBBTFrameOffset )
+		nNewFrames += m_JackTransportPos.bbt_offset;
+	m_transport.m_nFrames = nNewFrames;
+
+	/// offset between jack- and internal position
+	calculateFrameOffset();
+
+}
+
+///
+/// When jack_transport_start() is called, it takes effect from the next processing cycle.
+/// The location info from the timebase_master, if there is one, will not be available until the _next_ next cycle.
+/// The code must therefore wait one cycle before syncing up with timebase_master.
+///
 void JackDriver::updateTransportInfo()
 {
 	if (Preferences::getInstance()->m_bJackTransportMode ==  Preferences::USE_JACK_TRANSPORT) {
@@ -175,6 +240,10 @@ void JackDriver::updateTransportInfo()
 				break;
 
 			case JackTransportRolling:
+				if ( m_transport.m_status != TransportInfo::ROLLING && ( m_JackTransportPos.valid & JackPositionBBT ) ) {
+					must_relocate = 2;
+					warningLog( "Jack transport starting: Resyncing in 2 x Buffersize!!" );
+				}
 				m_transport.m_status = TransportInfo::ROLLING;
 				//infoLog( "[updateTransportInfo] ROLLING - frames: " + toString(m_transportPos.frame) );
 				break;
@@ -189,22 +258,37 @@ void JackDriver::updateTransportInfo()
 		}
 
 
-/*
 		// FIXME
 		// TickSize and BPM
 		if ( m_JackTransportPos.valid & JackPositionBBT ) {
-			if ( m_transport.m_nBPM != m_JackTransportPos.beats_per_minute ) {
-				m_transport.m_nBPM = m_JackTransportPos.beats_per_minute;
-				(Hydrogen::getInstance())->getSong()->m_fBPM = m_JackTransportPos.beats_per_minute;
-				warningLog( "[updateTransportInfo] new BPM from jack-transport: " + toString(m_JackTransportPos.beats_per_minute) );
+			float bpm = ( float )m_JackTransportPos.beats_per_minute;
+			if ( m_transport.m_nBPM != bpm ) {
+
+				warningLog( "Tempo change from jack-transport: " + toString( bpm ) );
+
+				m_transport.m_nBPM = bpm;
+
+				must_relocate = 1; // The tempo change has happened somewhere during the previous cycle; relocate right away.
+
+				// Hydrogen::getInstance()->setBPM( m_JackTransportPos.beats_per_minute ); // unnecessary, as Song->m_BPM gets updated in audioEngine_process_transport (after calling this function)
 			}
 		}
-*/
 
-		if ( m_transport.m_nFrames != m_JackTransportPos.frame ) {
-			m_transport.m_nFrames = m_JackTransportPos.frame;
-			warningLog( "[updateTransportInfo] internal frames != jack transport frames. resync!" );
+		if ( m_transport.m_nFrames + bbt_frame_offset != m_JackTransportPos.frame ) {
+			if ( ( m_JackTransportPos.valid & JackPositionBBT ) && must_relocate == 0 ) {
+				warningLog( "Frame offset mismatch; triggering resync in 2 cycles" );
+				must_relocate = 2;
+			} else { // NOTE There's no timebase_master. If audioEngine_process_checkBPMChanged handled a tempo change during last cycle, the offset doesn't match.
+				m_transport.m_nFrames = m_JackTransportPos.frame - bbt_frame_offset;
+// 				bbt_frame_offset = 0;
+			}
 		}
+		if ( must_relocate == 1 ) {
+			warningLog( "Resyncing!" );
+			relocateBBT();
+		}
+
+		if ( must_relocate > 0 ) must_relocate--;
 	}
 }
 
@@ -242,6 +326,11 @@ int JackDriver::init(unsigned nBufferSize)
 	output_port_name_1 = Preferences::getInstance()->m_sJackPortName1;
 	output_port_name_2 = Preferences::getInstance()->m_sJackPortName2;
 
+	if ( ( client = jack_client_open( "hydrogen", JackNullOption, NULL ) ) == NULL ) {
+		warningLog( "Error: can't start JACK server" );
+		return 3;
+	}
+
 	// check if another hydrogen instance is connected to jack
 	if ( (client = jack_client_new( "hydrogen-tmp" ) ) == 0 ) {
 		warningLog("Jack server not running?");
@@ -256,7 +345,7 @@ int JackDriver::init(unsigned nBufferSize)
 		int nColonPos = sPort.find( ":" );
 		string sClient = sPort.substr( 0, nColonPos );
 		bool bClientExist = false;
-		for ( int j = 0; j < clientList.size(); j++ ) {
+		for ( int j = 0; j < (int)clientList.size(); j++ ) {
 			if ( sClient == clientList[ j ] ) {
 				bClientExist = true;
 				break;
@@ -267,6 +356,7 @@ int JackDriver::init(unsigned nBufferSize)
 		}
 		nPort++;
 	}
+	free(readports);
 	jack_client_close( client );
 
 	string sClientName = "";
@@ -275,7 +365,7 @@ int JackDriver::init(unsigned nBufferSize)
 	//	sprintf( clientName, "Hydrogen-%d", getpid() );
 		sClientName = "Hydrogen-" + toString( nInstance );
 		bool bExist = false;
-		for ( int i = 0; i < clientList.size(); i++ ) {
+		for ( int i = 0; i < (int)clientList.size(); i++ ) {
 			if ( sClientName == clientList[ i ] ){
 				bExist = true;
 				break;
@@ -325,25 +415,6 @@ int JackDriver::init(unsigned nBufferSize)
 		return 4;
 	}
 
-
-	/* create MAX_INSTRUMENTS dedicated channel output ports */
-	if (track_out_flag) {
-		char tmpbuf[255];
-
-		for (unsigned int n=0; n < MAX_INSTRUMENTS; ++n) {
-			snprintf (tmpbuf, sizeof(tmpbuf), "track_out_%d_L", n+1);
-			track_output_ports_L[n] = jack_port_register ( client, tmpbuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
-			if (track_output_ports_L[n] == NULL) {
-				( Hydrogen::getInstance() )->raiseError( Hydrogen::JACK_ERROR_IN_PORT_REGISTER );
-			}
-
-			snprintf (tmpbuf, sizeof(tmpbuf), "track_out_%d_R", n+1);
-			track_output_ports_R[n] = jack_port_register ( client, tmpbuf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
-			if (track_output_ports_R[n] == NULL) {
-				( Hydrogen::getInstance() )->raiseError( Hydrogen::JACK_ERROR_IN_PORT_REGISTER );
-			}
-		}
-	}
 
 	// clear buffers
 //	jack_default_audio_sample_t *out_L = (jack_default_audio_sample_t *) jack_port_get_buffer (output_port_1, jack_server_bufferSize);
