/* W sample player (w) 12/95 by Eero Tamminen
 *
 * Uses the MiNT's /dev/audio device by Kay Roemer (author of MiNTNet).
 */

#include <stdlib.h>
#include <stdio.h>
#include <stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <ioctl.h>
#include <string.h>
#include <malloc.h>
#include <Wlib.h>
#include "audios.h"		/* ioctl controls */
#include "buttons.h"		/* interfase button images */

/* define some error messages */
#define ALERT_SAMPLE	"Load a sample first!"
#define ALERT_LOAD	"Unable to load sample!"
#define ALERT_MEM	"Not enough memory!"

#define DEV_AUDIO	"/dev/audio"

/* Used W font */
static WFONT *Font;

#define FONT_NAME	"fixed9.wfnt"	/* has to be fixed width one */
#define FONT_W		6
#define FONT_H		9	/* should be less than STRIP_HEIGHT (16) */

/* W window */
static WWIN *Window;

#define WIN_KIND	(W_MOVE | W_TITLE | EV_MOUSE | EV_KEYS)
#define WIN_NAME	" W Sample Player "

#define WIN_W		(STRIP_WIDTH + 24 * FONT_W)
#define WIN_H		(FONT_H * 2 + STRIP_HEIGHT)
#define MAX_NAMELEN	(WIN_W / FONT_W)

static struct
{
  int handle;
  long format;
  long rate;
  long lenght;
  void *data;
} audio;


/* ------------------------------------
 * Function prototypes.
 */
static int  prg_init(char *program);
static void prg_exit(int signal);

static int  which_button(short x, short y);
static void change_rate(long rate);
static void load_sample(char *file);
static void play_sample(void);
static void message(char *msg);
static void wait_winput(void);

void main(int argc, char *argv[])
{
  short idx = 1, mx, my;

  if(--argc < 1)
  {
    fprintf(stderr, "usage: %s sample [sample2 [sample3...]]\n", *argv);
    prg_exit(-1);
  }

  if(!prg_init(*argv))
    prg_exit(-1);

  load_sample(argv[idx]);
  for(;;)
  {
    wait_winput();
    w_querymousepos(Window, &mx, &my);

    switch(which_button(mx, my))
    {
      case B_LOAD:
        idx = idx % argc + 1;
	load_sample(argv[idx]);
	break;

      case B_PLAY:
	play_sample();
	break;

      case B_UP:
        change_rate(1024L);
	break;

      case B_DOWN:
        change_rate(-1024L);
	break;
    }
    /* invert to normal */
    which_button(mx, my);
  }
}

/* initialize W and /dev/audio */
static int prg_init(char *program)
{
  if(!w_init())
  {
    fprintf(stderr, "%s: can't connect to wserver\n", program);
    return 0;
  }

  if(!(Window = w_create(WIN_W, WIN_H, WIN_KIND)))
  {
    fprintf(stderr, "%s: can't create window\n", program);
    return 0;
  }

  if(!(Font = w_loadfont(FONT_NAME)))
  {
    fprintf(stderr, "%s: can't load " FONT_NAME "\n", program);
    return 0;
  }
  w_setfont(Window, Font);

  if(w_open(Window, -1, -1) < 0)
  {
    fprintf(stderr, "%s: can't open window\n", program);
    return 0;
  }
  w_settitle(Window, WIN_NAME);
  w_putblock(&controls, 0, 0, STRIP_WIDTH, STRIP_HEIGHT,
             Window, 0, WIN_H - STRIP_HEIGHT);
  w_hline(Window, STRIP_WIDTH + 1, WIN_H - STRIP_HEIGHT, WIN_W - 1);


  /* open audiodev for reading & writing */
  if((audio.handle = open(DEV_AUDIO, O_RDWR)) < 0)
  {
    fprintf(stderr, "%s: /dev/audio not available!\n", program);
    return 0;
  }

  /* configure */
  ioctl(audio.handle, AIOCGSPEED, &audio.rate);

  return 1;
}

/* close down and exit */
static void prg_exit(int signal)
{
  if(Font)
    w_unloadfont(Font);

  if(Window)
  {
    w_close(Window);
    w_delete(Window);
  }

  if(audio.data)
    free(audio.data);

  if(audio.handle)
    close(audio.handle);

  exit(signal);
}


/* what the user selected? Invert... */
static int which_button(short x, short y)
{
  int i, b = -1;

  if(y < (WIN_H - STRIP_HEIGHT) || x >= STRIP_WIDTH)
    return b;

  w_setmode(Window, M_INVERS);
  for(i = 1; i <= BUTTONS; i++)
  {
    if(x < offset[i])
    {
      b = i-1;
      w_pbox(Window, offset[b], WIN_H - STRIP_HEIGHT,
             offset[i] - offset[b], STRIP_HEIGHT - 1);
      break;
    }
  }
  w_flush();
  return b;
}


/* change the playing speed and output the value into the window */
static void change_rate(long change)
{
  char info[MAX_NAMELEN];

  long old_rate = audio.rate;
  do
  {
    audio.rate += change;
    ioctl(audio.handle, AIOCSSPEED, (void *)audio.rate);
    ioctl(audio.handle, AIOCGSPEED, &audio.rate);
    change <<= 1;
  } while(audio.rate == old_rate && change &&
    (change < 0 ? -change : change) < old_rate);
  /* step size depends on the h/w available */

  /* show information (with value rounding) */
  sprintf(info, "%3dkB = %4.1fs at %2ldkHz", (audio.lenght + 512) / 1024, (float)audio.lenght / audio.rate + 0.05, (audio.rate + 512) / 1024);
  w_setmode(Window, M_DRAW);
  w_printstring(Window, STRIP_WIDTH + FONT_W, WIN_H - (STRIP_HEIGHT - FONT_H) / 2 - FONT_H + 1, info);
}

/* select, allocate load and display sample */
static void load_sample(char *file)
{
  struct stat fst;
  FILE *fp;

  if(!(fp = fopen(file, "rb")))
    message(ALERT_LOAD);
  else
  {
    /* get sample */
    stat(file, &fst);
    audio.lenght = fst.st_size;
    if(audio.data)
      free(audio.data);
    if(!(audio.data = malloc(audio.lenght)))
      message(ALERT_MEM);
    else
    {
      fread(audio.data, (size_t)1, (size_t)audio.lenght, fp);
      message(file);
    }
    fclose(fp);
    change_rate(0L);	/* lenght & time into the window */
  }
}

/* play the sample through audiodev */
static void play_sample(void)
{
  if(!audio.data)
  {
    message(ALERT_SAMPLE);
    return;
  }
  /* play sample */
  write(audio.handle, audio.data, audio.lenght);

  /* wait for sample to end */
  ioctl(audio.handle, AIOCSYNC, (void *)0L);
}

/* output sample name or message */
static void message(char *msg)
{
  int name_len;
      
  w_setmode(Window, M_CLEAR);
  w_pbox(Window, 0, FONT_H/2, WIN_W-1, FONT_W);
  w_setmode(Window, M_DRAW);

  /* output the message onto the window */
  if((name_len = strlen(msg)) < MAX_NAMELEN)
    w_printstring(Window, (WIN_W - name_len * FONT_W)/2, FONT_H/2, msg);
  else
  {
    w_printstring(Window, (WIN_W - MAX_NAMELEN * FONT_W)/2, FONT_H/2, "...");
    w_printstring(Window, (WIN_W - MAX_NAMELEN * FONT_W)/2 + 3*FONT_W, FONT_H/2, &msg[name_len - MAX_NAMELEN+4]);
  }
}

static void wait_winput(void)
{
  WEVENT *ev;

  /* until no events, mouse button press */
  while((ev = w_queryevent(NULL, NULL, NULL, 0)) && ev->type == EVENT_MPRESS);

  /* until mouse button pressed or exited by a W event or with the ESC key */
  while(ev = w_queryevent(NULL, NULL, NULL, -1), ev->type != EVENT_MPRESS)
    if((ev->type == EVENT_GADGET && ev->key == GADGET_EXIT) ||
       (ev->type == EVENT_KEY && (ev->key & 0xFF) == '\033'))
      prg_exit(0);
}
