
#include <jack/jack.h>
#include <jack/transport.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


/*
  be timebase master



*/


jack_client_t *client;

float time_beats_per_bar = 4.0;
float time_beat_type = 4.0;
double time_ticks_per_beat = 1920.0;
double time_beats_per_minute = 120.0;
volatile int time_reset = 1;		/* true when time values change */

void timebase(jack_transport_state_t state, jack_nframes_t nframes, 
	      jack_position_t *pos, int new_pos, void *arg)
{
	double min;			/* minutes since frame 0 */
	long abs_tick;			/* ticks since frame 0 */
	long abs_beat;			/* beats since frame 0 */

	if (new_pos || time_reset) {

		pos->valid = JackPositionBBT;
		pos->beats_per_bar = time_beats_per_bar;
		pos->beat_type = time_beat_type;
		pos->ticks_per_beat = time_ticks_per_beat;
		pos->beats_per_minute = time_beats_per_minute;

		time_reset = 0;		/* time change complete */

		/* Compute BBT info from frame number.  This is relatively
		 * simple here, but would become complex if we supported tempo
		 * or time signature changes at specific locations in the
		 * transport timeline. */

		min = pos->frame / ((double) pos->frame_rate * 60.0);
		abs_tick = min * pos->beats_per_minute * pos->ticks_per_beat;
		abs_beat = abs_tick / pos->ticks_per_beat;

		pos->bar = abs_beat / pos->beats_per_bar;
		pos->beat = abs_beat - (pos->bar * pos->beats_per_bar) + 1;
		pos->tick = abs_tick - (abs_beat * pos->ticks_per_beat);
		pos->bar_start_tick = pos->bar * pos->beats_per_bar *
			pos->ticks_per_beat;
		pos->bar++;		/* adjust start to bar 1 */



	} else {

		/* Compute BBT info based on previous period. */
		pos->tick +=
			nframes * pos->ticks_per_beat * pos->beats_per_minute
			/ (pos->frame_rate * 60);

		while (pos->tick >= pos->ticks_per_beat) {
			pos->tick -= pos->ticks_per_beat;
			if (++pos->beat > pos->beats_per_bar) {
				pos->beat = 1;
				++pos->bar;
				pos->bar_start_tick +=
					pos->beats_per_bar
					* pos->ticks_per_beat;
			}
		}
	}
}



void jack_shutdown(void *arg)
{
	printf("JACK shut down, exiting ...\n");
	exit(1);
}


 
void print_jack_pos( jack_position_t* jack_pos ){

  printf( "print_jack_pos()\n" );
  printf( "    bar  [%d]\n", jack_pos->bar  );
  printf( "    beat [%d]\n", jack_pos->beat );		
  printf( "    tick [%d]\n", jack_pos->tick );
  printf( "    bar_start_tick   [%lf]\n", jack_pos->bar_start_tick );
  printf( "    beats_per_bar    [%f]\n", jack_pos->beats_per_bar );
  printf( "    beat_type        [%f]\n", jack_pos->beat_type );
  printf( "    ticks_per_beat   [%lf]\n", jack_pos->ticks_per_beat );
  printf( "    beats_per_minute [%lf]\n", jack_pos->beats_per_minute );
  printf( "    frame_time       [%lf]\n", jack_pos->frame_time );
  printf( "    next_time        [%lf]\n", jack_pos->next_time );
}


int jack_sync_callback(jack_transport_state_t state, 
					   jack_position_t *pos, void *arg)
{
  printf( "jack_sync_callback() " );
  
  switch ( state ){
      
      case JackTransportStopped:
          printf( "[JackTransportStopped]\n" ); break;
      case JackTransportRolling:
          printf( "[JackTransportRolling]\n" ); break;
          
      case JackTransportStarting:
      {
          printf( "[JackTransportStarting]\n" );
          break;
      }
        
  }

  print_jack_pos( pos );

  return true;

}




int main ( void )
{

 

  /* become a new client of the JACK server */
  if ((client = jack_client_new("transport tester")) == 0) {
	fprintf(stderr, "jack server not running?\n");
	return 1;
  }

  jack_on_shutdown(client, jack_shutdown, 0);
  jack_set_sync_callback(client, jack_sync_callback, NULL);

  if (jack_activate(client)) {
	fprintf(stderr, "cannot activate client");
	return 1;
  }

  //bool cond = false; /* true if we want to fail if there is already a master */
  //if (jack_set_timebase_callback(client, cond, timebase, NULL) != 0){
  //printf("Unable to take over timebase or there is already a master.\n");
//	exit(1);
  // }
  
  jack_position_t pos;

  pos.valid = JackPositionBBT;

  pos.bar = 0;
  pos.beat = 0;
  pos.tick = 0;

  pos.beats_per_bar = time_beats_per_bar;
  pos.beat_type = time_beat_type;
  pos.ticks_per_beat = time_ticks_per_beat;
  pos.beats_per_minute = time_beats_per_minute;
  pos.bar_start_tick = 0.0;
  

  jack_transport_state_t js;
  
  while(1){
      
      js = jack_transport_query( client, &pos );


      switch ( js ){
      
          case JackTransportStopped:
              printf( "[Stopped  ] " ); break;
          case JackTransportRolling:
              printf( "[Rolling  ] " ); break;
          case JackTransportStarting:
              printf( "[Starting ] " ); break;
      }
      
      printf( " bbb [%3d:%2d:%4d] framerate[%d] frame[%d] valid flags[%x]\n",
              pos.bar, pos.beat, pos.tick,
              pos.frame_rate, pos.frame,
              pos.valid );
      
      usleep(10000);
      
      
  }

 
  //jack_transport_stop (client);
  jack_release_timebase(client);
  jack_client_close(client);

  return 0;
}
