
#include "project.h"

#include <esd.h>
#include <linux/soundcard.h>

typedef struct Wp_struct
{
  int esd;
  FILE *file;
  char *filename;
  enum wavep_state state;
  char *buf;
  int buflen;
  off_t size;
  float voll;
  float volr;
  int lv;
  int rv;
}
 *Wp;


float
mult_to_vol (float m)
{
  if (m == 0.0)
    return 0.0;

  m = log10 (m);
  m += 2.5;
  m = m / 3.0;

  return m;
}

float
vol_to_mult (float m)
{
  if (m == 0.0)
    return 0.0;

  m *= 3.0;
  m -= 2.5;

  m = pow (10.0, m);

  return m;
}


struct wavep_time
make_time (int i)
{
  struct wavep_time s;

  s.secs = i;

  s.bmins = i / 60;
  s.bsecs = i % 60;

  return s;
}

void *
wavep_create (void)
{
  Wp w = (Wp) malloc (sizeof (struct Wp_struct));

  bzero (w, sizeof (struct Wp_struct));

  w->buf = malloc (ESD_BUF_SIZE);
  w->buflen = ESD_BUF_SIZE;

  w->voll = 0.8;
  w->volr = 0.8;
  w->lv = (int) (256.0 * w->voll);
  w->rv = (int) (256.0 * w->volr);

  return w;
}

int
wavep_fd (void *iw)
{
  Wp w = (Wp) iw;

  return w->esd;
}

void
wavep_setvol (void *iw, float l, float r)
{
  Wp w = (Wp) iw;

  w->voll = vol_to_mult (l);
  w->volr = vol_to_mult (r);
  w->lv = (int) (256.0 * w->voll);
  w->rv = (int) (256.0 * w->volr);
}


void
wavep_seek (void *iw, int whence)
{
  Wp w = (Wp) iw;

  if (w->state != WAVE_PLAYING)
    return;

  whence *= RATE;

  fseek (w->file, whence, SEEK_SET);
}

void
wavep_stop (void *iw)
{
  Wp w = (Wp) iw;

  if (w->filename)
    free (w->filename);
  if (w->esd >= 0)
    close (w->esd);
  if (w->file)
    fclose (w->file);

  w->file = NULL;

  w->esd = -1;
  w->filename = NULL;

  w->state = WAVE_STOPPED;
}

void
wavep_play (void *iw, char *name)
{
  Wp w = (Wp) iw;
  wavep_stop (iw);

  w->filename = strdup (name);
  w->file = fopen (w->filename, "r");

  if (!w->file)
    {
      wavep_stop (iw);
      return;
    }

  w->esd = esd_play_stream (ESD_BITS16 | ESD_STEREO, 44100, NULL, "wave");
  w->state = WAVE_PLAYING;

  fseek (w->file, 0, SEEK_END);
  w->size = ftell (w->file);
  fseek (w->file, 0, SEEK_SET);


}

void
wavep_record (void *iw, char *name)
{
  Wp w = (Wp) iw;
  int test, value;
  char *foo;

  wavep_stop (iw);


  w->filename = strdup (name);
  w->file = fopen (w->filename, "w");

  if (!w->file)
    {
      wavep_stop (iw);
      return;
    }

  foo = hifi_getrecorddsp ();


  if (foo)
    {
      w->esd = open (foo, O_RDONLY);


      test = AFMT_S16_LE;
      ioctl (w->esd, SNDCTL_DSP_SETFMT, &test);

      test = 1;
      ioctl (w->esd, SNDCTL_DSP_STEREO, &test);

      test = 44100;
      ioctl (w->esd, SNDCTL_DSP_SPEED, &test);

      printf ("opened audio fd %d\n", w->esd);


    }
  else
    {
      w->esd =
        esd_record_stream (ESD_BITS16 | ESD_STEREO, 44100, NULL, "wave");
    }

  w->state = WAVE_RECORDING;
  w->size = 0;


}

struct wavep_status
wavep_status (void *iw)
{
  Wp w = (Wp) iw;
  struct wavep_status s;

  if (w->file)
    {
      s.length = make_time (w->size / RATE);
      s.position = make_time (ftell (w->file) / RATE);
    }
  else
    {
      s.length = make_time (0);
      s.position = make_time (0);
    }


  s.voll = mult_to_vol (w->voll);
  s.volr = mult_to_vol (w->volr);

  s.filename = w->filename;
  s.state = w->state;
  return s;
}

int
wavep_dispatch (void *iw)
{
  Wp w = (Wp) iw;
  int i;


  switch (w->state)
    {
    case WAVE_PLAYING:
      i = fread (w->buf, 1, w->buflen, w->file);

      if (i <= 0)
        {
          wavep_stop (iw);
          return 1;
        }

      {
        int lv = w->lv;
        int rv = w->rv;
        int k;
        int j = i >> 2;
        int16_t *ptr = (int16_t *) w->buf;

        while (j--)
          {
            k = (*ptr) * lv;
            *ptr = (u_int16_t) (k >> 8);
            ptr++;
            k = (*ptr) * rv;
            *ptr = (u_int16_t) (k >> 8);
            ptr++;
          }

      }

      write (w->esd, w->buf, i);


      break;
    case WAVE_RECORDING:
      i = read (w->esd, w->buf, w->buflen);

      if (i < 0)
        {
          wavep_stop (iw);
          return 1;
        }

      fwrite (w->buf, 1, i, w->file);

      w->size += i;
      break;
    default:
      ;
    }


  return 0;
}
