/**
   \file ftMono.c Mono / Stereo / Differential driver for VSOS
   \author Henrik Herranen, VLSI Solution Oy
*/

#include <vo_stdio.h>
#include <string.h>
#include <stdlib.h>
#include <apploader.h>
#include <ctype.h>
#include <vstypes.h>
#include <vs1005h.h>
#include <timers.h>
#include <vsos.h>
#include <sysmemory.h>
#include <ringbuf.h>
#include <aucommon.h>
#include <mutex.h>
#include "../ftMono.h"
#include "../miscAsm.h"

extern FILE *audio_orig;

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

u_int16 ftMonoFlags = 0;
s_int16 sampleWords = 1;

IOCTL_RESULT AudioIoctl(register __i0 VO_FILE *self, s_int16 request,
			IOCTL_ARGUMENT argp) {
#if 0
  fprintf(stderr, "Ioctl(%p, %d, 0x%p)\n", self, request, argp);
#endif

  switch (request) {
  case IOCTL_RESTART:
    ftMonoFlags = 0;
    sampleWords = 1;
    break;
  case IOCTL_AUDIO_SET_BITS:
    sampleWords = ((s_int16)argp) >> 4;
    break;
  case IOCTL_AUDIO_SET_RATE_AND_BITS:
    sampleWords = 1 + (*((s_int32 *)argp) < 0);
    break;
  case IOCTL_AUDIO_GET_MONO:
    return ftMonoFlags;
    break;
  case IOCTL_AUDIO_SET_MONO:
    ftMonoFlags = (u_int16)argp;
    return 0;
    break;
  }

  return ioctl(audio_orig, request, argp);
}


#ifdef USE_STDAUDIOIN
u_int16 AudioRead(register __i0 VO_FILE *self, u_int16 *data,
		  u_int16 destinationIndex, u_int16 bytes) {
  /* Read input data from original audio driver. */
  u_int16 retVal = fread(data, 1, bytes>>1, audio_orig);
  int stereoSamples = retVal>>sampleWords;

  /* We have read the input data. Now do the mono/stereo manipulation. */
  
  switch (ftMonoFlags & MONO_MODE_MASK) {
  case MONO_MODE_LL:
    ringcpy(data+2*sampleWords-1, 2*sampleWords, data+sampleWords-1, 2*sampleWords, stereoSamples);
    break;
  case MONO_MODE_RR:
    ringcpy(data+sampleWords-1, 2*sampleWords, data+2*sampleWords-1, 2*sampleWords, stereoSamples);
    break;
  case MONO_MODE_RL:
    SwapLR(data, sampleWords, stereoSamples);
    break;
  case MONO_MODE_MONO:
    Monoize(data, sampleWords, stereoSamples);
    break;
  }

  if (ftMonoFlags & MONO_INV_L) InvertCh(data, sampleWords, stereoSamples);
  if (ftMonoFlags & MONO_INV_R) InvertCh(data+sampleWords, sampleWords, stereoSamples);
   
   return bytes;
}
#endif /* USE_STDAUDIOIN */



#ifdef USE_STDAUDIOOUT
u_int16 AudioWrite(register __i0 VO_FILE *self, u_int16 *data,
		   u_int16 sourceIndex, u_int16 bytes) {
  int stereoSamples = bytes>>(1+sampleWords);

  /* Do the mono/stereo manipulation. */
  
  switch (ftMonoFlags & MONO_MODE_MASK) {
  case MONO_MODE_LL:
    ringcpy(data+2*sampleWords-1, 2*sampleWords, data+sampleWords-1, 2*sampleWords, stereoSamples);
    break;
  case MONO_MODE_RR:
    ringcpy(data+sampleWords-1, 2*sampleWords, data+2*sampleWords-1, 2*sampleWords, stereoSamples);
    break;
  case MONO_MODE_RL:
    SwapLR(data, sampleWords, stereoSamples);
    break;
  case MONO_MODE_MONO:
    Monoize(data, sampleWords, stereoSamples);
    break;
  }

  if (ftMonoFlags & MONO_INV_L) InvertCh(data, sampleWords, stereoSamples);
  if (ftMonoFlags & MONO_INV_R) InvertCh(data+sampleWords, sampleWords, stereoSamples);

  /* Write manipulated data to original audio driver. */
  
  fwrite(data, 2, bytes>>2, audio_orig);

  return bytes;
}
#endif /* USE_STDAUDIOOUT */



FILEOPS audioFileOps = {
  NULL, /* Open() */
  NULL, /* Close() */
  AudioIoctl, /* Ioctl() */
#ifdef USE_STDAUDIOIN
  AudioRead,  /* Read()  (if this is in the input path)  */
#else
  CommonOkResultFunction,
#endif
#ifdef USE_STDAUDIOOUT
  AudioWrite,  /* Write() (if this is in the output path) */
#else
  CommonOkResultFunction,
#endif
};

SIMPLE_FILE audioFile = {
#ifdef USE_STDAUDIOOUT
  __MASK_PRESENT|__MASK_OPEN|__MASK_WRITABLE|__MASK_FILE, /* Flags */
#else
  __MASK_PRESENT|__MASK_OPEN|__MASK_READABLE|__MASK_FILE, /* Flags */
#endif
  NULL, /* Identify() */
  &audioFileOps,
  0, /* pos */
  0, /* ungetc_buffer */
  NULL, /* dev */
};
