/**************************************************************************
*
* FunktrackerGOLD - By Jason Nunn
*
* "/dev/dsp" channel mixxer.
*
* Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia
*
**************************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#ifdef Linux
 #include <sys/soundcard.h>
#elif FreeBSD
 #include <machine/soundcard.h>
#endif
#pragma pack(1)
#include "everything.h"
#pragma pack()

extern void funk_tracker(void);
extern unsigned char funk_card_type; 
 
tchmix chmix[MAXIMUM_CHANNELS];

                   /*00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 */
int shift_table[] = {-1,-1,-1,-1, 9,10,10,10,10,11,11,11,11,11,11,11,11};
int dsp_fp;
int precision;        /*8 or 16 bit*/
int sample_precision; /*8 or 16 bit*/
int bpm_rate;
int hz_rate;
int stereo;
int sampling_rate;
int no_active_channels = 4;

unsigned long mix_buffer_size;
long *left_mix_buffer = NULL;
long *right_mix_buffer = NULL;

#pragma pack(1)
uDB *final_mix_buffer08 = NULL;
uDW *final_mix_buffer16 = NULL;
uDD *final_mix_buffer32 = NULL;
#pragma pack()

#ifdef DIGITAL_ECHOING
#define echo_dly_factor  256 /* @ 22050hz*/
#define echo_buffer_size (512 * 16)
#define echo_mask        (echo_buffer_size - 1)
int echo_seg;
#endif

void DAC0816_channel_mixxer(int chan_no,unsigned long *lbuf,unsigned long *rbuf);
void DAC1616_channel_mixxer(int chan_no,unsigned long *lbuf,unsigned long *rbuf);
void (*channel_mixxer)(int,unsigned long *,unsigned long *) = DAC0816_channel_mixxer;

/**************************************************************************
*
**************************************************************************/
void calc_mix_buffer(int bpm)
{
  hz_rate = 2 * (bpm / 5);
  mix_buffer_size = (int)(((double)sampling_rate / hz_rate) + 0.5);
}

/**************************************************************************
*
* Clear Mixx buffers
*
**************************************************************************/
void init_dsp_buffers(void)
{
#ifdef DIGITAL_ECHOING
  register int chan;
#endif
  register int x;

  calc_mix_buffer(bpm_rate);
  ioctl(dsp_fp,SNDCTL_DSP_RESET);
  for(x = 0;x < mix_buffer_size;x++)
  {
    *(left_mix_buffer + x) = 0;
    *(right_mix_buffer + x) = 0;

    if(precision == 8)
      if(stereo)
        *(final_mix_buffer16 + x) = 0;
      else
        *(final_mix_buffer08 + x) = 0;
    else
      if(stereo)
        *(final_mix_buffer32 + x) = 0;
      else
        *(final_mix_buffer16 + x) = 0;
  }
#ifdef DIGITAL_ECHOING
  for(chan = 0;chan < MAXIMUM_CHANNELS;chan++)
    for(x = 0;x < echo_buffer_size;x++)
      *(chmix[chan].echo_buffer + x) = 0;
#endif
  if(sample_precision == 8)
    channel_mixxer = DAC0816_channel_mixxer;
  else
    channel_mixxer = DAC1616_channel_mixxer;
}

/**************************************************************************
*
* Open up (blocking) /dev/dsp
*
**************************************************************************/
void close_dsp_dealloc(void)
{
#ifdef DIGITAL_ECHOING
  register int x;
#endif

  if(left_mix_buffer != NULL) free(left_mix_buffer);
  if(right_mix_buffer != NULL) free(right_mix_buffer);
  if(final_mix_buffer08 != NULL) free(final_mix_buffer08);
  if(final_mix_buffer16 != NULL) free(final_mix_buffer16);
  if(final_mix_buffer32 != NULL) free(final_mix_buffer32);

#ifdef DIGITAL_ECHOING
  for(x = 0;x < MAXIMUM_CHANNELS;x++)
    if(chmix[x].echo_buffer != NULL)
      free(chmix[x].echo_buffer);
#endif
}

void *open_dsp_alloc_buf(unsigned long length)
{
  void *buffer;

  buffer = malloc(length);
  if(buffer == NULL)
  {
    close_dsp_dealloc();
    printf("Error: Couldn't allocate mixxer buffer. Program aborted.\n");
  }
  return buffer;
}

int open_dsp(int srate,int prec,int st,int frames,int fragment)
{
  int frag = 0x00400009;
#ifdef DIGITAL_ECHOING
  register int x;
#endif

  sample_precision = 8;
  sampling_rate = srate;
  precision = prec;
  stereo = st;
  if(sampling_rate < 6000) sampling_rate = 6000;
  if(sampling_rate > 44100) sampling_rate = 44100;
  calc_mix_buffer(MIN_BPM_RATE);

#ifdef DIGITAL_ECHOING
  echo_seg = (echo_dly_factor * sampling_rate) / 22050;
#endif
  printf("Mix length: %d (%dhz)\n",
    (int)mix_buffer_size,sampling_rate);

  left_mix_buffer = open_dsp_alloc_buf(mix_buffer_size * sizeof(long));
  if(left_mix_buffer == NULL) return 0;
  right_mix_buffer = open_dsp_alloc_buf(mix_buffer_size * sizeof(long));
  if(right_mix_buffer == NULL) return 0;
  if(precision == 8)
  {
    funk_card_type = FKCARD_VOX08;
    if(stereo)
    {
      final_mix_buffer16 = open_dsp_alloc_buf(mix_buffer_size * 2);
      if(final_mix_buffer16 == NULL) return 0;
    }
    else
    {
      final_mix_buffer08 = open_dsp_alloc_buf(mix_buffer_size * 1);
      if(final_mix_buffer08 == NULL) return 0;
    }
  }
  else
  {
    funk_card_type = FKCARD_VOX16;
    if(stereo)
    {
      final_mix_buffer32 = open_dsp_alloc_buf(mix_buffer_size * 4);
      if(final_mix_buffer32 == NULL) return 0;
    }
    else
    {
      final_mix_buffer16 = open_dsp_alloc_buf(mix_buffer_size * 2);
      if(final_mix_buffer16 == NULL) return 0;
    }
  }
#ifdef DIGITAL_ECHOING
  for(x = 0;x < MAXIMUM_CHANNELS;x++)
  {
    chmix[x].echo_buffer = open_dsp_alloc_buf(echo_buffer_size * 4);
    if(chmix[x].echo_buffer == NULL) return 0;
  }
#endif

  if((dsp_fp = open("/dev/dsp",O_WRONLY,0)) == -1)
  {
    printf("Error: Couldn't open /dev/dsp. Program aborted.\n");
    return 0;
  }
  printf("Opened /dev/dsp.. ");
  if(ioctl(dsp_fp,SNDCTL_DSP_SAMPLESIZE,&precision) == -1)
  {
    printf("\nError: Could not set /dev/dsp sample precision. Program aborted.\n");
    return 0;
  }
  printf("%d bit",precision);
  frag = (frames << 16) + fragment;
  printf(" (Buffer: %d frames @ %d bytes) ",frames,1 << fragment);
  if(ioctl(dsp_fp,SNDCTL_DSP_SETFRAGMENT,&frag) == -1)
  {
    printf("\nError: Fragment buffering. Program aborted.\n");
    return 0;
  }
  if(ioctl(dsp_fp,SNDCTL_DSP_STEREO,&stereo) == -1)
  {
    printf("\nError: Could not set /dev/dsp stereo/mono setting. Program aborted.\n");
    return 0;
  }
  if(stereo)
    printf("stereo");
  else
    printf("mono");
  if(ioctl(dsp_fp,SNDCTL_DSP_SPEED,&sampling_rate) == -1)
  {
    printf("\nError: Setting frequency. Program aborted.\n");
    return 0;
  }
  printf("\nSampling at %d Hertz\n",sampling_rate);

  return 1;
}

void close_dsp(void)
{
  ioctl(dsp_fp,SNDCTL_DSP_RESET);
  close(dsp_fp);
  close_dsp_dealloc();
}

/**************************************************************************
080808080808080808080808080808080808080808080808080808080808080808080808080
080808080808080808080808080808080808080808080808080808080808080808080808080
080808080808080808080808080808080808080808080808080808080808080808080808080
*
* 08 bit into 16bit unit channel DAC buffer Mixer
*
* 00
* ||_____ loop sample
* |______ DAC sample play
*
080808080808080808080808080808080808080808080808080808080808080808080808080
080808080808080808080808080808080808080808080808080808080808080808080808080
080808080808080808080808080808080808080808080808080808080808080808080808080
**************************************************************************/
void DAC0816_channel_mixxer(int chan_no,
                            unsigned long *lbuf,
                            unsigned long *rbuf)
{
  register unsigned long sample_no;
  register int sam;
#ifdef DIGITAL_ECHOING
  register long sam_reverb;
  register unsigned int echo_rpos = chmix[chan_no].echo_delay * echo_seg;
#endif
  for(sample_no = 0;sample_no < mix_buffer_size;sample_no++)
  {
    if(chmix[chan_no].funkctrl & 0x2)
#pragma pack(1)
      sam = *((sDB *)chmix[chan_no].sample_addr +
            (unsigned long)chmix[chan_no].sample_ptr);
#pragma pack()
    else
      sam = 0;
#ifdef DIGITAL_ECHOING
    sam_reverb = *(chmix[chan_no].echo_buffer +
                 ((chmix[chan_no].echo_ptr - echo_rpos) & echo_mask));
    *(chmix[chan_no].echo_buffer +
     (chmix[chan_no].echo_ptr & echo_mask)) =
      sam + ((sam_reverb * chmix[chan_no].echo_feedback) >> 4);
    sam += (sam_reverb * chmix[chan_no].echo_decay) >> 4;
    chmix[chan_no].echo_ptr++;
#endif
    *(lbuf++) += sam * chmix[chan_no].left_volume;
    *(rbuf++) += sam * chmix[chan_no].right_volume;
    chmix[chan_no].sample_ptr += chmix[chan_no].freq;
    if(chmix[chan_no].sample_ptr >= chmix[chan_no].length)
    {
      if(chmix[chan_no].funkctrl & 0x1)
        chmix[chan_no].sample_ptr = chmix[chan_no].start;
      else
        chmix[chan_no].funkctrl = 0;
    }
  }
}

/**************************************************************************
161616161616161616161616161616161616161616161616161616161616161616161616161
161616161616161616161616161616161616161616161616161616161616161616161616161
161616161616161616161616161616161616161616161616161616161616161616161616161
*
* 16 bit into 16bit unit channel DAC buffer Mixer
*
* 00
* ||_____ loop sample
* |______ DAC sample play
*
161616161616161616161616161616161616161616161616161616161616161616161616161
161616161616161616161616161616161616161616161616161616161616161616161616161
161616161616161616161616161616161616161616161616161616161616161616161616161
**************************************************************************/
void DAC1616_channel_mixxer(int chan_no,
                            unsigned long *lbuf,
                            unsigned long *rbuf)
{
  register unsigned long sample_no;
  register int sam;
#ifdef DIGITAL_ECHOING
  register long sam_reverb;
  register unsigned int echo_rpos = chmix[chan_no].echo_delay * echo_seg;
#endif
  for(sample_no = 0;sample_no < mix_buffer_size;sample_no++)
  {
    if(chmix[chan_no].funkctrl & 0x2)
#pragma pack(1)
      sam = *((sDW *)chmix[chan_no].sample_addr +
            (unsigned long)chmix[chan_no].sample_ptr);
#pragma pack()
    else
      sam = 0;
#ifdef DIGITAL_ECHOING
    sam_reverb = *(chmix[chan_no].echo_buffer +
                 ((chmix[chan_no].echo_ptr - echo_rpos) & echo_mask));
    *(chmix[chan_no].echo_buffer +
     (chmix[chan_no].echo_ptr & echo_mask)) =
      sam + ((sam_reverb * chmix[chan_no].echo_feedback) >> 4);
    sam += (sam_reverb * chmix[chan_no].echo_decay) >> 4;
    chmix[chan_no].echo_ptr++;
#endif
    *(lbuf++) += (sam * chmix[chan_no].left_volume) >> 8;
    *(rbuf++) += (sam * chmix[chan_no].right_volume) >> 8;
    chmix[chan_no].sample_ptr += chmix[chan_no].freq;
    if(chmix[chan_no].sample_ptr >= chmix[chan_no].length)
    {
      if(chmix[chan_no].funkctrl & 0x1)
        chmix[chan_no].sample_ptr = chmix[chan_no].start;
      else
        chmix[chan_no].funkctrl = 0;
    }
  }
}

/***************************************************************************
*
* mixxer control (x bit samples mixxed into 8 bit mono output..god help us)
*
***************************************************************************/
void DSPx08_mono_mixxer(void)
{
  register int no;

  funk_tracker();
  for(no = 0;no < no_active_channels;no++)
    channel_mixxer(no,left_mix_buffer,right_mix_buffer);
  for(no = 0;no < mix_buffer_size;no++)
  {
    *(final_mix_buffer08 + no) =
      ((*(left_mix_buffer + no) +
      *(right_mix_buffer + no)) >> 9) ^ 0x80;
    *(left_mix_buffer + no) = 0;
    *(right_mix_buffer + no) = 0;
  }
  write(dsp_fp,final_mix_buffer08,mix_buffer_size);
}

/***************************************************************************
*
* mixxer control (x bit samples mixxed into 16 bit mono output)
*
***************************************************************************/
void DSPx16_mono_mixxer(void)
{
  register int no;

  funk_tracker();
  for(no = 0;no < no_active_channels;no++)
    channel_mixxer(no,left_mix_buffer,right_mix_buffer);
  for(no = 0;no < mix_buffer_size;no++)
  {
    *(final_mix_buffer16 + no) =
      (*(left_mix_buffer + no) + *(right_mix_buffer + no)) >> 1;
    *(left_mix_buffer + no) = 0;
    *(right_mix_buffer + no) = 0;
  }
  write(dsp_fp,final_mix_buffer16,mix_buffer_size * 2);
}

/***************************************************************************
*
* mixxer control (x bit samples mixxed into 8 bit stereo output)
*
***************************************************************************/
void DSPx08_stereo_mixxer(void)
{
  register int no;

  funk_tracker();
  for(no = 0;no < no_active_channels;no++)
    channel_mixxer(no,left_mix_buffer,right_mix_buffer);
  for(no = 0;no < mix_buffer_size;no++)
  {
    *(final_mix_buffer16 + no) =
      ((*(left_mix_buffer + no) & 0xff00) ^ 0x8000) +
      (((*(right_mix_buffer + no) & 0xff00) >> 8) ^ 0x80);
    *(left_mix_buffer + no) = 0;
    *(right_mix_buffer + no) = 0;
  }
  write(dsp_fp,final_mix_buffer16,mix_buffer_size * 2);
}

/***************************************************************************
*
* mixxer control (x bit samples mixxed into 16 bit stereo output)
*
***************************************************************************/
void DSPx16_stereo_mixxer(void)
{
  register int no;

  funk_tracker();
  for(no = 0;no < no_active_channels;no++)
    channel_mixxer(no,left_mix_buffer,right_mix_buffer);
  for(no = 0;no < mix_buffer_size;no++)
  {
    *(final_mix_buffer32 + no) =
      (*(left_mix_buffer + no) << 16) + (*(right_mix_buffer + no) & 0xffff);
    *(left_mix_buffer + no) = 0;
    *(right_mix_buffer + no) = 0;
  }
  write(dsp_fp,final_mix_buffer32,mix_buffer_size * 4);
}

/**************************************************************************
***************************************************************************
*
* This routines are hardware dependant routines. that translate real freq.
* and volume values into the specific card scalarity.
*
***************************************************************************
**************************************************************************/
void DSPi_freq_convert(int chan_no,long rfreq)
{
  chmix[chan_no].freq = (double)rfreq / sampling_rate;
}

void DSPi_volume_convert(int chan_no,int mvolume,int rvolume,int balance)
{
  chmix[chan_no].left_volume =
    (((rvolume * (~balance & 0xff)) >> 8) * mvolume) >>
    shift_table[no_active_channels];
  chmix[chan_no].right_volume =
    (((rvolume * balance) >> 8) * mvolume) >>
    shift_table[no_active_channels];
}

