Please consider a donation to the Higher Intellect project. See https://preterhuman.net/donate.php or the Donate to Higher Intellect page for more info.

Ltc.c

From Higher Intellect Vintage Wiki
Revision as of 09:23, 1 September 2020 by Netfreak (talk | contribs) (Created page with "<pre> /* * ltc.c - example of using dmLTC * * compile with -ldmedia and -laudio * * parses and prints out LTC codewords from audio input. * * works with sampling rates...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
/*
 * ltc.c - example of using dmLTC
 *
 * compile with -ldmedia and -laudio
 *
 * parses and prints out LTC codewords from audio input.
 *
 * works with sampling rates 22k and above, may work less than
 * that too.
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <string.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <sys/uio.h>

#include <dmedia/audio.h>
#include <dmedia/dmedia.h>
#include <sys/dmcommon.h>
#include <dmedia/dm_params.h>
#include <dmedia/dm_timecode.h>
#include <dmedia/dm_audio.h>
#include <dmedia/dm_ltc.h>

/* Utility Routines ---------------------------------------------------- */

void error_exit(char *format, ...)
{
  va_list ap;
  
  va_start(ap, format);
  vfprintf( stdout, format, ap );
  va_end(ap);

  fprintf( stdout, "\n" );
  exit(2);
}

void error(char *format, ...)
{
  va_list ap;
  
  va_start(ap, format);
  vfprintf( stdout, format, ap );
  va_end(ap);

  fprintf( stdout, "\n" );
}

void print_tc_type_name(int tc_type)
{
  switch (tc_type & DM_TC_FORMAT_MASK)
    {
    case DM_TC_FORMAT_NTSC: printf("NTSC "); break;
    case DM_TC_FORMAT_PAL:  printf("PAL ");  break;
    case DM_TC_FORMAT_FILM: printf("FILM "); break;
    }
  
  if ((tc_type & DM_TC_FORMAT_MASK) == DM_TC_FORMAT_NTSC)
    {
      switch (tc_type & DM_TC_DROP_MASK)
        {
        case DM_TC_NODROP:    printf("non-drop "); break;
        case DM_TC_DROPFRAME: printf("drop "); break;
        }
    }
  
  switch (tc_type & DM_TC_RATE_MASK)
    {
    case DM_TC_RATE_2997: printf("29.97 frame/sec"); break;
    case DM_TC_RATE_30:   printf("30 frame/sec"); break;
    case DM_TC_RATE_24:   printf("24 frame/sec"); break;
    case DM_TC_RATE_25:   printf("25 frame/sec"); break;
    }
  
  printf(" (0x%x)", tc_type);
}

/*
** DC -- "DM Params Check".  Wraps a call to a dmParams function that
** returns DM_FAILURE on error.
*/

#define DC(call) \
{ \
  if ( (call) != DM_SUCCESS ) \
    error_exit("DM_FAILURE for call \"%s\"", #call); \
}

/* main ------------------------------------------------------------- */

int main(void)
{
  /* 
   * LTC state 
   */
  DMLTCdecoder ltcdecoder;
  int tc_type;

  /*
   * AL state
   */
  stamp_t last_frontier_msc;
  ALconfig config;
  ALport port;
  char *intrbuf;

  /* these describe the currently buffered data */
  int nmscs;
  char *bufptr;
  
  int rate;
  int bytes_per_sample;
  float qseconds;
  
  /* first open audio path */
  
  config = ALnewconfig();  
  ALsetsampfmt(config, AL_SAMPFMT_TWOSCOMP);
  ALsetwidth(config, AL_SAMPLE_16);
  bytes_per_sample = 2;
  ALsetchannels(config, 1);
  
  /* get rate */
  {
    long pvbuf[2];
    pvbuf[0] = AL_INPUT_RATE;
    ALgetparams(AL_DEFAULT_DEVICE, pvbuf, 2);
    rate = pvbuf[1];
  }
  
  /* qseconds second queue size */
  
  qseconds = 1.0;
  
  ALsetqueuesize(config, 
                 qseconds * ALgetchannels(config) * rate);
  
  intrbuf = malloc(ALgetqueuesize(config) * bytes_per_sample);
  
  printf("Audio sampfmt: TWOSCOMP\n");
  printf("Audio width: %d bits\n", ALgetwidth(config) * 8);
  printf("Audio channels: %d\n", ALgetchannels(config));
  printf("Audio rate: %d Hz\n", rate);
  printf("Audio queuesize: %d samples\n", ALgetqueuesize(config));
  
  if (NULL == (port = ALopenport("ltc", "r", config)))
    error_exit("AL port open failed");
  
  /* set up ltc decoder */

  printf("LTC assuming code is DM_TC_30_ND\n");
  tc_type = DM_TC_30_ND;

  DC( dmLTCDecoderCreate(&ltcdecoder, tc_type) );
  {
    int width = ALgetwidth(config);
    int chans = ALgetchannels(config);
    DMparams *audioparams;
    
    DC( dmParamsCreate(&audioparams) );
    DC( dmParamsSetInt(audioparams, DM_AUDIO_CHANNELS, chans) );
    DC( dmParamsSetEnum(audioparams, DM_AUDIO_FORMAT, 
                        DM_AUDIO_TWOS_COMPLEMENT ) );
    switch (width)
      {
      case AL_SAMPLE_8:
        DC( dmParamsSetInt(audioparams, DM_AUDIO_WIDTH, 
                           DM_AUDIO_WIDTH_8) );
        break;
      case AL_SAMPLE_16:
        DC( dmParamsSetInt(audioparams, DM_AUDIO_WIDTH, 
                           DM_AUDIO_WIDTH_16) );
        break;
      case AL_SAMPLE_24:
        DC( dmParamsSetInt(audioparams, DM_AUDIO_WIDTH, 
                           DM_AUDIO_WIDTH_24) );
        break;
      }
    
    /* assumes first channel (0) */
    dmLTCDecoderSetParams(ltcdecoder, audioparams, 0);
    
    dmParamsDestroy(audioparams);
  }

  /* clear out rb */
  ALreadsamps(port, intrbuf, ALgetfilled(port));

  last_frontier_msc = -1;

  nmscs = 0; /* no audio intially buffered up */

  /* go! */

  while (1)
    {
      int n;
      assert(nmscs == 0);
      
      /* ---- get a buffer of new audio data */
      
      n = ALgetqueuesize(config);
      ALreadsamps(port, intrbuf, n);
      nmscs = n / ALgetchannels(config);
      bufptr = intrbuf;
      
      /* ---- check for overflow (optional but very good to do) */
      {
        stamp_t frontier_msc;
        
        if (-1 == ALgetframenumber(port, 
                                   (unsigned long long *)(&frontier_msc)))
          error_exit("ALgetframenumber failed.");
        
        if (last_frontier_msc != -1 &&
            frontier_msc != last_frontier_msc + nmscs)
          {
            error("we dropped %d audio frames of data",
                  frontier_msc - last_frontier_msc + nmscs);
          }
        
        last_frontier_msc = frontier_msc;
      }

      /* ---- parse as many codewords as we can from this buffer */

      while (1)
        {
          DMLTCcode codeword;
          int nmscs_before, nmscs_eaten;
          DMstatus ret;
          
          if (nmscs == 0) 
            {
              /* no more data to parse */
              break;
            }
          
          nmscs_before = nmscs;
          
          /* dmLTCDecode increments pointer and decrements nmscs */
          ret = dmLTCDecode(ltcdecoder, (void **)&bufptr, &nmscs, &codeword);
          
          nmscs_eaten = nmscs_before - nmscs;
          assert(nmscs_eaten <= nmscs_before && nmscs_eaten >= 0);
          
          if (DM_SUCCESS != ret) 
            {
              /* consumed all nmscs, found no LTC codeword */
              assert(nmscs_eaten == nmscs_before);
              assert(nmscs == 0);
              break;
            }

          /* got a codeword */
          {
            char tcstring[40];
            dmTCToString(tcstring, &codeword.tc);
            printf("got %s (", tcstring);
            print_tc_type_name(codeword.tc.tc_type);
            printf(")\n");
          }
        }
    }

  ALcloseport(port);
}