decode

LTC (linear timecode) decoder. Does not support drop-frame format. This implementation only works when the system clock is 48kHz or 96kHz, not at 44.1kHz!
Author: Johannes Taelman
License: BSD
Github: ltc/decode.axo

Inlets

frac32buffer ltc input

Outlets

bool32 sync detect

int32 hours

int32 minutes

int32 seconds

int32 frames

int32 sample offset to frame

Attributes

combo fps

Declaration
uint8_t h; // hour counter
uint8_t m; // minute counter
uint8_t s; // seconds counter
uint8_t f; // frame counter
int8_t b;  // bit counter
// <0 : syncing
// >=0 : reading
int32_t subsample;
uint8_t ss;      // sample counter
int32_t bo;      // bit output
int32_t psample; // previous sample
int32_t sexp;    // expected sample period in audio samples
uint32_t data[2];
uint32_t data2[2];
Init
h = 0;
m = 0;
s = 0;
f = 0;
b = -1;
ss = 0;
bo = 0;
sexp = (SAMPLERATE / 160) / attr_fps;
subsample = 0;
Control Rate
outlet_h = h;
outlet_m = m;
outlet_s = s;
outlet_f = f;
outlet_smp = subsample;
outlet_sync = 0;
Audio Rate
int32_t i1 = inlet_in >> 2;
int transition = ((psample > 0) != (i1 > 0));

if (b == -1) {
  if (transition) {
    b = -2;
    ss = 0; // beginning of sync word
  }
} else if (((b < -1) && (b > -26)) || (b == -27) || (b == -28)) {
  if ((ss > (sexp - 2)) && (ss <= (sexp + 2))) {
    if (transition) {
      b--;
      ss = 0;
      if (b == -29) {
        // f++;
        b = 0;
        data[0] = 0;
        data[1] = 0;
      } else if (b == -10) {
        // use this edge for subsample sync
        int32_t _i1 = i1;
        int32_t _i0 = psample;
        if (i1 < 0) {
          _i1 = -i1;
          _i0 = -psample;
        }
        subsample = ((_i0 << 6) / (_i0 - _i1)) + 64 * buffer_index;
        if (f == 0) {
          outlet_sync = 1;
        }
      }
    }
  } else if (ss > (sexp + 2)) {
    b = -1; // start searching again...
  }
} else if (b == -26) {
  if ((ss > (2 * sexp - 2)) && (ss <= (2 * sexp + 2))) {
    ss = 0;
    b--;
  } else if (ss > (2 * sexp + 2)) {
    // busted
    b = -1; // start searching again...
  }
} else if (b >= 0) {
  if ((ss > (sexp - 2)) && (ss <= (sexp + 2))) {
    if (transition) {
      if (b < 32) {
        data[0] |= 1 << b;
      } else {
        data[1] |= 1 << (b - 32);
      }
    }
  }
  if ((ss > (2 * sexp - 2)) && (ss <= (2 * sexp + 2))) {
    if (transition) {
      ss = 0;
      b++;
      if (b == 64) {
        f = (data[0] & 0x0F) + 10 * ((data[0] >> 8) & 0x03);
        s = ((data[0] >> 16) & 0x0F) + 10 * ((data[0] >> 24) & 0x07);
        m = (data[1] & 0x0F) + 10 * ((data[1] >> 8) & 0x07);
        h = ((data[1] >> 16) & 0x0F) + 10 * ((data[1] >> 24) & 0x03);
        data2[0] = data[0];
        data2[1] = data[1];
        //			f++;
        b = -1;
      }
    }
  } else if (ss > (2 * sexp + 2)) {
    // busted
    b = -1; // start searching again...
  }
}

psample = i1;
ss++;

Privacy

© 2024 Zrna Research