/******************************************/
/*
  audiodelay.cpp
  by Peter Meerwald, pmeerw@pmeerw.net, 2009.

  This program tests the delay between audio output and input.
*/
/******************************************/

#include "RtAudio.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>

const char *progname = "audiodelay";

typedef signed short  AUDIOSAMPLE_TYPE;
#define FORMAT RTAUDIO_SINT16

static volatile unsigned int nmeasurements = 0;
static float accum_measurements = 0.0f;

static int inout(void *out_buf, void *in_buf, unsigned int nbuf_frames,
           double stream_time, RtAudioStreamStatus status, void *data) {
  if (status) 
    printf("%s: stream over/underflow detected\n", progname);

  static unsigned int count_inout = 0;
  static struct timeval tv_start;
  static struct timeval tv_stop;

  AUDIOSAMPLE_TYPE *pout = (AUDIOSAMPLE_TYPE *)out_buf, *pin = (AUDIOSAMPLE_TYPE *)in_buf;

  memset(out_buf, 0, nbuf_frames*sizeof(AUDIOSAMPLE_TYPE) * 2);
  if ((count_inout % (512*128/nbuf_frames)) == 0) {
      for (unsigned int i = nbuf_frames/2-4; i < nbuf_frames/2+4; i++) {
        pout[2*i] = 32000;
        pout[2*i+1] = 32000;
      }
    
      gettimeofday(&tv_start, 0);
  }

  if (1) {
    for (unsigned int i = 0; i < nbuf_frames; i++) {
      if (pin[2*i] > 15000 || pin[2*i+1] > 15000) {

        gettimeofday(&tv_stop, 0);
      
        unsigned int msec = (tv_stop.tv_sec - tv_start.tv_sec) * 1000;
        if (tv_stop.tv_usec < tv_start.tv_usec)
          msec -= (tv_start.tv_usec - tv_stop.tv_usec) / 1000;
        else 
          msec += (tv_stop.tv_usec - tv_start.tv_usec) / 1000;

        accum_measurements += msec;
        nmeasurements++;
        printf("%s: measured delay %u ms, offset %u, average %.2f\n", progname, msec, i, accum_measurements / nmeasurements);
            
        break;
      }
    }
  }

  count_inout++;

  return 0;
}

int main(int argc, char *argv[]) {
  unsigned int channels = 2, sample_rate = 44100, buf_bytes, odevice = 0, idevice = 0, ioffset = 0, ooffset = 0;

  RtAudio adac;
  if (adac.getDeviceCount() < 1) {
    std::cout << "\nNo audio devices found!\n";
    exit(EXIT_FAILURE);
  }

  // Let RtAudio print messages to stderr.
  adac.showWarnings(true);

  // Set the same number of channels for both input and output.
  unsigned int buf_frames = 128;
  RtAudio::StreamParameters iparams, oparams;
  iparams.deviceId = idevice;
  iparams.nChannels = channels;
  iparams.firstChannel = ioffset;
  oparams.deviceId = odevice;
  oparams.nChannels = channels;
  oparams.firstChannel = ooffset;

  RtAudio::StreamOptions options;
  options.flags = 0; // RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_SCHEDULE_REALTIME
  options.numberOfBuffers = 2;
  options.priority = 0; // for RTAUDIO_SCHEDULE_REALTIME

  try {
    adac.openStream(&oparams, &iparams, FORMAT, sample_rate, &buf_frames, &inout, (void *)&buf_bytes, &options);
  }
  catch (RtError& e) {
    printf("%s: %s\n", progname, e.getMessage().c_str());
    exit(EXIT_FAILURE);
  }

  buf_bytes = buf_frames * channels * sizeof(AUDIOSAMPLE_TYPE);

  try {
    adac.startStream();

    printf("%s: stream latency %ld frames, %d sample rate\n", progname, adac.getStreamLatency(), adac.getStreamSampleRate());

    printf("%s: running ... press <enter> to quit (%d buffer frames)\n", progname, buf_frames);
    getchar();

    // Stop the stream.
    adac.stopStream();
  }
  catch (RtError& e) {
    printf("%s: %s\n", progname, e.getMessage().c_str());
    goto cleanup;
  }

cleanup:
  if (adac.isStreamOpen()) 
    adac.closeStream();

  return 0;
}
