Logo Search packages:      
Sourcecode: jack-tools version File versions  Download package

jack.osc.c

/***** jack.osc.c - (c) rohan drape, 2004-2006 *****/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <pthread.h>
#include <sys/time.h>

#include "common/byte-order.h"
#include "common/client.h"
#include "common/failure.h"
#include "common/jack-client.h"
#include "common/jack-port.h"
#include "common/memory.h"
#include "common/network.h"
#include "common/observe-signal.h"
#include "common/osc.h"
#include "common/print.h"
#include "common/time-current.h"
#include "common/time-ntp.h"
#include "common/time-timeval.h"

#define REQUEST_TICK        0x00000001
#define REQUEST_PULSE       0x00000002
#define REQUEST_CORRECTION  0x00000004
#define REQUEST_TRANSPORT   0x00000008
#define REQUEST_ALL         0xFFFFFFFF

00032 struct jackosc
{
  f64 fps;              /* Frames per second (ie. sample rate) */
  f64 spf;              /* Seconds per frame */
  f64 ppc;              /* Pulses per cycle */
  f64 pt;               /* Pulse type */
  f64 ppm;              /* Pulses per minute */
  f64 ppf;              /* Pulses per frame */
  f64 tpf;              /* Ticks per frame */
  f64 pulse;                  /* Pulse clock */
  i64 frm;              /* Frame clock (jack.osc) */
  i64 j_frm;                  /* Frame clock (jackd) */
  u64 ntp;              /* NTP clock */
  f64 utc;              /* UTC clock */
  i8 roll;
  i32 correct_interval; 
  i32 correct_n;
  i8 fps_alt;
  jack_client_t *client;
  int fd;
  client_register_t *cr;
  pthread_t osc_thread;
};

#define COMMON_SETUP(n)                   \
  u8 packet[256];                   \
  osc_data_t o[n];                        \
  o[0].t = ntp;                           \
  o[1].d = utc;                           \
  o[2].h = frm;

#define COMMON_SEND(addr,dsc,incl)                          \
  int packet_sz;                                      \
  packet_sz = osc_construct_message(addr, dsc, o, packet, 256);         \
  sendto_client_register(d->fd, d->cr, packet, packet_sz, incl); 

void send_jck_drift(struct jackosc *d, 
                u64 ntp, f64 utc, i64 frm, 
                i64 ntp_dif, f64 utc_dif)
{
  COMMON_SETUP(5);
  o[3].h = ntp_dif;
  o[4].d = utc_dif;
  COMMON_SEND("/drift", ",tdhhd", REQUEST_CORRECTION);
}

void send_jck_tick(struct jackosc *d, 
               u64 ntp, f64 utc, i64 frm, 
               i64 frame, f64 pulse)
{
  COMMON_SETUP(5);
  o[3].h = frame;
  o[4].d = pulse;
  COMMON_SEND("/tick", ",tdhhd", REQUEST_TICK);
}

void send_jck_current(int fd, struct sockaddr_in address, 
                  u64 ntp, f64 utc, i64 frm, 
                  i64 frame, f64 pulse)
{
  COMMON_SETUP(5);
  o[3].h = frame;
  o[4].d = pulse;
  int packet_sz;
  packet_sz = osc_construct_message("/current.reply", ",tdhhd", 
                            o, packet, 256);
  if(packet_sz) {
    sendto_exactly(fd, packet, packet_sz, address);
  }
}

void send_jck_pulse(struct jackosc *d, 
                u64 ntp, f64 utc, i64 frm, 
                u64 p_ntp, f64 p_utc, i64 p_frm, i32 pulse)
{
  COMMON_SETUP(7);
  o[3].t = p_ntp;
  o[4].d = p_utc;
  o[5].h = p_frm;
  o[6].i = pulse;
  COMMON_SEND("/pulse", ",tdhtdhi", REQUEST_PULSE);
}

void send_jck_transport(struct jackosc *d, 
                  u64 ntp, f64 utc, i64 frm, 
                  f64 fps, f64 ppm, f64 ppc, f64 pt, 
                  i32 rolling)
{
  COMMON_SETUP(8);
  o[3].d = fps;
  o[4].d = ppm;
  o[5].d = ppc;
  o[6].d = pt;
  o[7].i = rolling;
  COMMON_SEND("/transport", ",tdhddddi", REQUEST_TRANSPORT);
}

void send_jck_status(int fd, struct sockaddr_in address, 
                 f64 fps, f64 ppm, f64 ppc, f64 pt, 
                 i32 rolling)
{
  u8 packet[256];
  osc_data_t o[5];
  int packet_sz;
  o[0].d = fps;
  o[1].d = ppm;
  o[2].d = ppc;
  o[3].d = pt;
  o[4].i = rolling;
  packet_sz = osc_construct_message("/status.reply", ",ddddi", 
                            o, packet, 256);
  if(packet_sz) {
    sendto_exactly(fd, packet, packet_sz, address);
  }
}

#define OSC_PARSE_MSG(command,types)                        \
  osc_parse_message(command, types, packet, packet_sz, o)

void *jackosc_osc_thread_procedure(void *PTR)
{
  struct jackosc *d = (struct jackosc *) PTR;
  while(!observe_end_of_process()) {
    if(fd_wait(d->fd, 500000)) {
      struct sockaddr_in addr;
      socklen_t addr_len = sizeof(addr);
      const int packet_extent = 64;
      u8 packet[packet_extent];
      i32 packet_sz = xrecvfrom(d->fd, packet, 64, 0, 
                        (struct sockaddr *)&addr, &addr_len);
      osc_data_t o[4];
      if(OSC_PARSE_MSG("/receive", ",i")) {
      edit_client_register(d->cr, addr, o[0].i);
      } else if(OSC_PARSE_MSG("/receive_at", ",iis")) {
      struct sockaddr_in ra_addr;
      init_sockaddr_in(&ra_addr, o[2].s, (i16)o[1].i);
      edit_client_register(d->cr, ra_addr, o[0].i);
      } else if(OSC_PARSE_MSG("/status", ",")) {
      send_jck_status(d->fd, addr, 
                  d->fps, d->ppm, d->ppc, d->pt, d->roll);
      } else if(OSC_PARSE_MSG("/current", ",")) {
      send_jck_current(d->fd, addr, 
                   d->ntp, d->utc, d->frm, d->j_frm, d->pulse);
      } else if(OSC_PARSE_MSG("/start", ",")) {
      jack_transport_start(d->client);
      } else if(OSC_PARSE_MSG("/stop", ",")) {
      jack_transport_stop(d->client);
      } else if(OSC_PARSE_MSG("/locate", ",f")) {
      jack_transport_locate(d->client, o[0].f * d->fps);
      } else if(OSC_PARSE_MSG("/connect", ",ss")) {
      jack_port_connect_named(d->client, o[0].s, o[1].s);
      } else if(OSC_PARSE_MSG("/disconnect", ",ss")) {
      jack_port_disconnect_named(d->client, o[0].s, o[1].s);
      } else {
      eprintf("%s: dropped packet: %8s\n", __func__, packet);
      }
    }
  }
  return NULL;
}

inline f64 get_ticks_per_frame(f64 ticks_per_pulse, 
                         f64 pulses_per_minute, 
                         f64 frames_per_second)
{
  f64 pulses_per_second = pulses_per_minute / 60.0;
  f64 ticks_per_second = ticks_per_pulse * pulses_per_second;
  return ticks_per_second / frames_per_second; 
}

inline f64 get_pulses_per_frame(f64 pulses_per_minute, 
                        f64 frames_per_second)
{
  f64 pulses_per_second = pulses_per_minute / 60.0;
  return pulses_per_second / frames_per_second; 
}

int jackosc_process(jack_nframes_t nframes, void *PTR)
{
  struct jackosc *d =(struct jackosc *) PTR;
  if(d->correct_n >= d->correct_interval) {
    struct timeval t = current_time_as_utc_timeval();
    u64 c_ntp = utc_timeval_to_ntp(t);
    f64 c_utc = timeval_to_real(t);
    i64 c_ntp_dif = c_ntp - d->ntp;
    f64 c_utc_dif = c_utc - d->utc;
    send_jck_drift(d, d->ntp, d->utc, d->frm, c_ntp_dif, c_utc_dif);
    d->ntp = c_ntp;
    d->utc = c_utc;
    d->correct_n = 0;
  }
  jack_position_t p;
  jack_transport_state_t s = jack_transport_query(d->client, &p);
  if(( s & JackTransportRolling)!= d->roll) {
    d->roll = s & JackTransportRolling;
    send_jck_transport(d, d->ntp, d->utc, d->frm,
                   d->fps, d->ppm, d->ppc, d->pt, d->roll);
  }
  if(d->fps_alt) {
    d->ppf = get_pulses_per_frame(d->ppm, d->fps);
    d->tpf = get_ticks_per_frame(p.ticks_per_beat, d->ppm, d->fps);
    send_jck_transport(d, d->ntp, d->utc, d->frm,
                   d->fps, d->ppm, d->ppc, d->pt, d->roll);
    d->fps_alt = 0;
  }
  if(( p.valid & JackPositionBBT)) {
    if(d->ppm != p.beats_per_minute) {
      d->ppm = p.beats_per_minute;
      d->ppf = get_pulses_per_frame(d->ppm, d->fps);
      d->tpf = get_ticks_per_frame(p.ticks_per_beat, d->ppm, d->fps);
      send_jck_transport(d, d->ntp, d->utc, d->frm,
                   d->fps, d->ppm, d->ppc, d->pt, d->roll);
    }
    if(d->ppc != p.beats_per_bar || d->pt != p.beat_type) {
      d->ppc = p.beats_per_bar;
      d->pt = p.beat_type;
      send_jck_transport(d, d->ntp, d->utc, d->frm,
                   d->fps, d->ppm, d->ppc, d->pt, d->roll);
    }
    if(d->roll && 
       (p.tick == 0 || 
      ((f64)p.tick +(floorf((f64)nframes * d->tpf)))> (f64)p.ticks_per_beat)) {
      i32 p_tick_off =(p.tick == 0)? 0 : p.ticks_per_beat - p.tick;
      f64 p_frm_off = (f64)p_tick_off * d->tpf;
      f64 p_frm = (f64)d->frm + p_frm_off;
      f64 p_utc = d->utc +(p_frm_off * d->spf);
      u64 p_ntp = utc_real_to_ntp(p_utc);
      i32 p_pulse =(p.tick == 0)? p.beat : p.beat + 1;
      if((f64)p_pulse > d->ppc) {
      p_pulse = 1;
      }
      send_jck_pulse(d, d->ntp, d->utc, d->frm, p_ntp, p_utc, (i64)floor(p_frm), p_pulse);
      /* Correct pulse accumulator. */
      d->pulse = (f64)p_pulse -(ceil(p_frm_off)* d->ppf);
      if(d->pulse < 1.0) {
      d->pulse += d->ppc;
      }
    }
    /* jck_tk is sent after jck_pl to report the corrected pulse. */
    send_jck_tick(d, d->ntp, d->utc, d->frm, (i64)p.frame, d->pulse);
  }
  d->frm += nframes;
  d->j_frm = (i64)p.frame;
  d->utc += (f64)nframes * d->spf;
  d->ntp = utc_real_to_ntp(d->utc);
  d->pulse += (f64)nframes * d->ppf;
  if(d->pulse > d->ppc + 1.0) {
    d->pulse -= d->ppc;
  }
  d->correct_n += 1;
  return 0;
}

int jackosc_fps_handler(jack_nframes_t fps, void *PTR)
{
  struct jackosc *d =(struct jackosc *) PTR;
  d->fps = (f64) fps;
  d->fps_alt = 1;
  return 0;
}

void jackosc_usage(void)
{
  eprintf("Usage: jack.osc [ options ]\n");
  eprintf("   -c  Drift correction interval in periods (default=64).\n");
  eprintf("   -p  Port number (default=57130).\n");
  FAILURE;
}

int main(int argc, char *argv[])
{
  observe_signals();
  struct jackosc d;
  d.ppc = 0.0;
  d.pt = 0.0;
  d.ppm = 0.0;
  d.ppf = 0.0;
  d.tpf = 0.0;
  d.pulse = 0.0;
  d.frm = 0;
  d.ntp = 0;
  d.utc = 0.0;
  d.roll = 0;
  /* Ensure that a correction occurs when the process starts. */
  d.correct_interval = 64;
  d.correct_n = d.correct_interval;
  d.cr = alloc_client_register(16);
  int port_n = 57130;
  int c;
  while(( c = getopt(argc, argv, "c:hp:")) != -1) {
    switch(c) {
    case 'c':
      d.correct_interval = atoi(optarg);
      d.correct_n = d.correct_interval;
      break;
    case 'h':
      jackosc_usage();
      break;
    case 'p':
      port_n = atoi(optarg);
      break;
    default:
      eprintf("%s: Illegal option %c.\n", __func__, c);
      jackosc_usage();
      break;
    }
  }
  d.fd = socket_udp(0);
  bind_inet(d.fd, NULL, port_n);
  d.client = jack_client_unique("jack.osc");
  jack_set_error_function(jack_client_minimal_error_handler);
  jack_set_sample_rate_callback(d.client, jackosc_fps_handler, &d);
  jack_on_shutdown(d.client, jack_client_minimal_shutdown_handler, 0);
  jack_set_process_callback(d.client, jackosc_process, &d);
  d.fps = jack_get_sample_rate(d.client);
  d.fps_alt = 0;
  d.spf = 1.0 / d.fps;
  jack_client_activate(d.client);
  pthread_create(&(d.osc_thread), NULL, jackosc_osc_thread_procedure, &d);
  pthread_join(d.osc_thread, NULL);
  jack_client_close(d.client);
  free_client_register(d.cr);
  exit(0);
  return 0;
}  

Generated by  Doxygen 1.6.0   Back to index