/*
	wavplay.c
	30.05.99 tn

	simple wavfile-player. plays only 44.1khz,16bit,stereo files.
	gui-mode to fully control all play-functions via external interface 

	works fine on little and big endian machines.

	21.11.01 tn
	MacosX code thx to Shawn Hsiao <phsiao@mac.com> 
*/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "largefile.h"

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#if defined(linux) || defined(__CYGWIN32__)
# include <getopt.h>
#endif
#include <sys/stat.h>
#if defined(linux) || defined(__FreeBSD__)
# include <sys/soundcard.h>
# include <sys/ioctl.h>
#endif
#if defined(sun) || defined(__OpenBSD__)
# include <sys/ioctl.h>
# include <sys/audioio.h>
#endif
#ifdef aix 
#include <errno.h>
#include <sys/audio.h>
#endif
#ifdef hpux
# ifndef hpux_alib
#  include <sys/audio.h>
# else
#  include <sys/socket.h>
#  include <netdb.h>
#  include <netinet/in.h>
#  include <netinet/tcp.h>
#  include <Alib.h>
# endif
#endif

#include <glib.h>
#include "xcdroast.h"

#if (defined(__MACH__) && defined(__APPLE__))
#include <CoreAudio/CoreAudio.h>
#include <machine/limits.h>
#include <pthread.h>
#endif

#if defined(__sgi)
#include <dmedia/audio.h>
#endif

static off_t waveBase ;
static gint abuf_size;
static guchar *audiobuf;

gint read_line(gint fd, gchar *ptr, gint maxlen);
off_t is_std_wav_file(int f, off_t *offset);


#if defined(linux) || defined(__FreeBSD__)
#define DEFAULT_AUDIO_DEVICE "/dev/dsp"
#elif defined (aix)
#define DEFAULT_AUDIO_DEVICE ""
#elif defined(__sgi)
#define DEFAULT_AUDIO_DEVICE ""
#else
#define DEFAULT_AUDIO_DEVICE "/dev/audio"
#endif

#define DEFAULT_BUFFER_SIZE 22050 

#if (defined(__MACH__) && defined(__APPLE__))

#define BUF_SIZE 4096 

static AudioDeviceID gOutputDeviceID;

static float OutputDataBuf[BUF_SIZE];
static int OutputWroteSamples = 0;

static pthread_mutex_t mutexOutput;
static pthread_cond_t condOutput;

static int audioPlaybackStarted = 0;
static int coreaudio_has_output_device = 0;
static int coreaudio_init = 0;

OSStatus PlaybackIOProc(AudioDeviceID inDevice,
                        const AudioTimeStamp *inNow,
                        const AudioBufferList *inInputData,
                        const AudioTimeStamp *inInputTime,
                        AudioBufferList *outOutputData,
                        const AudioTimeStamp *inOutputTime,
                        void *inClientData)
{
  float *bufPtr = outOutputData->mBuffers[0].mData;
  int i;

  pthread_mutex_lock(&mutexOutput);

  for (i = 0; i < OutputWroteSamples; i++)
    bufPtr[i] = OutputDataBuf[i];
  for ( ; i < BUF_SIZE; i++)
    bufPtr[i] = 0;
  OutputWroteSamples = 0;

  pthread_mutex_unlock(&mutexOutput);
  pthread_cond_signal(&condOutput);

  return (kAudioHardwareNoError);
}


/*
 * This is called to reset the device status.
 * Returns -2 to indicate the device failed to initialize;
 * returns  -1 means any of rate/size/{mono,stereo} mismatched.
 */

gint open_macosx_audio() {
#define LEN_DEVICE_NAME 64
  OSStatus status;
  UInt32 propertySize, bufferByteCount;
  char deviceName[LEN_DEVICE_NAME];
  struct AudioStreamBasicDescription streamDesc;
  int rval;

  /*
   * We only need to do this once, the rest are taken cared by
   * disable/enable calback.
   */
  if (coreaudio_init) {
    return (0);
  }

  /* get default output device */
  propertySize = sizeof(gOutputDeviceID);
  status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
                                    &propertySize,
                                    &gOutputDeviceID);
  if (status) {
    fprintf(stderr, "get default output device failed, status = %d\n",
            (int)status);
    return (-2);
  }

  if (gOutputDeviceID != kAudioDeviceUnknown) {
    /* got default output device */
    coreaudio_has_output_device = 1;

    /* get output device name */
    propertySize = sizeof(char)*LEN_DEVICE_NAME;
    status = AudioDeviceGetProperty(gOutputDeviceID,
                                    1,
                                    0,
                                    kAudioDevicePropertyDeviceName,
                                    &propertySize,
                                    deviceName);

    if (status) {
      fprintf(stderr, "get device name failed, status = %d\n",
              (int)status);
      return (-2);
    }

    /* get output format */
    propertySize = sizeof(struct AudioStreamBasicDescription);
    status = AudioDeviceGetProperty(gOutputDeviceID,
                                    1,
                                    0,
                                    kAudioDevicePropertyStreamFormat,
                                    &propertySize,
                                    &streamDesc);
    if (status) {
      fprintf(stderr, "get device property failed, status = %d\n",
              (int)status);
      return (-2);
    }

    if ((streamDesc.mSampleRate != 44100.0) ||
        (streamDesc.mFormatID != kAudioFormatLinearPCM) ||
       !(streamDesc.mFormatFlags & kLinearPCMFormatFlagIsFloat) ||
        (streamDesc.mChannelsPerFrame != 2))
    {
      fprintf (stderr, "unsupported device.\n");
      return (-2);
    }

    /* set buffer size */
    bufferByteCount = BUF_SIZE * sizeof(float);
    propertySize = sizeof(bufferByteCount);
    status = AudioDeviceSetProperty(gOutputDeviceID,
                                    0,
                                    0,
                                    0,
                                    kAudioDevicePropertyBufferSize,
                                    propertySize,
                                    &bufferByteCount);
    if (status) {
      fprintf(stderr, "set device property failed, status = %d\n",
              (int)status);
    }

/*
    fprintf(stderr, "using device %s for output:\n", deviceName);
    fprintf(stderr, "\twith sample rate %f, %ld channels and %ld-bit sample\n",
            streamDesc.mSampleRate,
            streamDesc.mChannelsPerFrame,
            streamDesc.mBitsPerChannel);
*/

    rval = pthread_mutex_init(&mutexOutput, NULL);
    if (rval) {
      fprintf(stderr, "mutex init failed\n");
      return (-1);
    }
    rval = pthread_cond_init(&condOutput, NULL);
    if (rval) {
      fprintf(stderr, "condition init failed\n");
      return (-1);
    }

    /* Registers PlaybackIOProc with the device without activating it. */
    status = AudioDeviceAddIOProc(gOutputDeviceID, PlaybackIOProc, (void *)1);
  }

  if (!coreaudio_has_output_device) {
    fprintf(stderr, "unknown output device.\n");
    return (-2);
  }

  /* Indicates the initialization is done */
  coreaudio_init = 1;

  abuf_size = DEFAULT_BUFFER_SIZE;
 
  return 0;
}

void audio_close()
{
  OSStatus status;

  if (coreaudio_has_output_device && audioPlaybackStarted) {
    status = AudioDeviceStop(gOutputDeviceID, PlaybackIOProc);
    audioPlaybackStarted = 0;
  }
  return;
}

int audio_write( void *buffer, int buf_size )
{
  OSStatus status;
  float scale = 1.0 / SHRT_MAX;
  int remain_to_write = buf_size;

  if (!coreaudio_has_output_device)
    return -1;

  if (!audioPlaybackStarted) {
    status = AudioDeviceStart(gOutputDeviceID, PlaybackIOProc);
    audioPlaybackStarted = 1;
  }

  while (remain_to_write)
  {
    pthread_mutex_lock(&mutexOutput);

    while(OutputWroteSamples == BUF_SIZE)
      pthread_cond_wait(&condOutput, &mutexOutput);

    {
      short *src_data = (short *)buffer + (buf_size - remain_to_write) / sizeof(short);
      float *dst_data = OutputDataBuf + OutputWroteSamples;
      int src_samples = remain_to_write / sizeof(short);
      int dst_samples = BUF_SIZE - OutputWroteSamples;
      int n = (dst_samples < src_samples) ? dst_samples : src_samples;
      int i;

      for (i = 0; i < n; i++)
        dst_data[i] = scale * src_data[i];

      OutputWroteSamples += n;
      remain_to_write -= n * sizeof(short);
    }

    pthread_mutex_unlock(&mutexOutput);
  }

  return (buf_size);
}


#endif


#if defined(linux) || defined(__FreeBSD__)

/* open the sound-device of linux and set cd-quality */

gint open_linux_audio(gchar *dev) {
gint audio;
gint tmp;
gint samplesize = 16;
gint dsp_stereo = 1;
gint dsp_speed = 44100;
gint flags;

	/* First try open with O_NONBLOCK so it doesn't hang */
	audio = open (dev, (O_WRONLY | O_NONBLOCK),0);
	if (audio == -1) {
		/* error opening sound device */
		return -1;
	}

	/* now undo NONBLOCK setting again */
	flags = fcntl(audio,F_GETFL);
	fcntl(audio,F_SETFL,flags & ~O_NONBLOCK);

	tmp = samplesize; 
	ioctl(audio, SNDCTL_DSP_SAMPLESIZE, &samplesize);	
	if (tmp != samplesize) {
		/* error setting samplesize */
		g_warning("Unable to set samplesize to 16 bit\n");
		return -1;
	}

	if (ioctl (audio, SNDCTL_DSP_STEREO, &dsp_stereo)==-1) {
		g_warning("Unable to set audio to stereo\n");
		return -1;
	}
	
	if (ioctl (audio, SNDCTL_DSP_SPEED, &dsp_speed) == -1) {
		g_warning("Unable to set audio to 44.1 kHz\n");
		return -1;
	}

	if (ioctl (audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size) == -1) {
		g_warning("Unable to get blocksize for audio-device\n");
		return -1;
	}

	return audio;
}

#endif

#if defined(sun) || defined(__OpenBSD__)

/* open the sound-device of solaris and set cd-quality */

gint open_solaris_audio(gchar *dev) {
gint audio;
audio_info_t info;
gint flags;

	/* First try open with O_NONBLOCK so it doesn't hang */
	if ((audio = open(dev, (O_WRONLY | O_NONBLOCK))) == -1) {
		/* error opening sound device */
		return -1;
	}

	/* now undo NONBLOCK setting again */
	flags = fcntl(audio,F_GETFL);
	fcntl(audio,F_SETFL,flags & ~O_NONBLOCK);

	abuf_size = DEFAULT_BUFFER_SIZE;

	AUDIO_INITINFO(&info);
	info.play.sample_rate = 44100;
	info.play.channels = 2;
	info.play.precision = 16;
	info.play.encoding = AUDIO_ENCODING_LINEAR;
	info.play.buffer_size = abuf_size;
#ifndef __OpenBSD__
	info.output_muted = 0;
#endif

	if (ioctl(audio, AUDIO_SETINFO, &info) == -1) {
		g_warning("Unable to set audio parameters\n");
		return -1;
	}	

	return audio;
}

#endif

#ifdef aix
/* open the audio device of an IBM RS/6000 with AIX Ultimedia Services
   and set cd-quality */

gint open_aix_audio(gchar *dev) {
audio_init ainfo;
audio_control  acontrol;
audio_change   achange;
gint audio;

        if (strlen (dev) == 0) {
                if(getenv("AUDIODEV")) {
                        strcpy (dev, getenv("AUDIODEV"));
                        audio = open (dev, O_WRONLY);
                } else {
                       /* Try to use the device of a machine with PCI bus */
                        strcpy (dev, "/dev/paud0/1");
                        audio = open (dev, O_WRONLY);
                        if ((audio == -1) & (errno == ENOENT)) {
                               /* Try the device of a machine with MCA    */
                                /* bus as last default                     */
                                strcpy (dev, "/dev/baud0/1");
                                audio = open (dev, O_WRONLY);
                       }
              }
       } else {
               /* Try the device given in the commandline */
                audio = open (dev, O_WRONLY);
       }

       if(dev < 0) {
               /* error opening audio device */
               return -1;
       }

        /* Initialize the default audio description  */
       /* (the size of the buffer is given in msec) */
        memset (&ainfo, '\0', sizeof (audio_init));
        ainfo.srate            = 44100;
        ainfo.channels         = 2;
        ainfo.mode             = PCM;
        ainfo.bits_per_sample  = 16;
        ainfo.flags            = BIG_ENDIAN | TWOS_COMPLEMENT;
        ainfo.operation        = PLAY;
        ainfo.bsize            = 8 * 1000 *DEFAULT_BUFFER_SIZE 
               / (ainfo.srate * ainfo.channels * ainfo.bits_per_sample);
 
        if (ioctl (audio, AUDIO_INIT, &ainfo) == -1) {
               g_warning("Unable to set default audio parameters\n");
               return -1;
       }

       memset ( & acontrol, '\0', sizeof (acontrol));
  
       /* Tell the audio device to start output     */
       acontrol.ioctl_request   = AUDIO_START;
       acontrol.request_info    = NULL;
       acontrol.position        = 0;

       if (ioctl (audio, AUDIO_CONTROL, & acontrol) == -1) {
               g_warning("Unable to start audio operation\n");
               return -1;
       }
       abuf_size = DEFAULT_BUFFER_SIZE;

       memset ( & achange, '\0', sizeof (achange));
       memset ( & acontrol, '\0', sizeof (acontrol));
       
        /* set the output to line out + internal speaker          */
       /* else the output device will be set to the predifined   */
       /* value in the cde environment.                          */
       achange.balance        = AUDIO_IGNORE;
       achange.balance_delay  = AUDIO_IGNORE;
       achange.volume         = AUDIO_IGNORE;
       achange.volume_delay   = AUDIO_IGNORE;
       achange.input          = AUDIO_IGNORE;
       achange.output         = EXTERNAL_SPEAKER;
       /* achange.output         = INTERNAL_SPEAKER */
       /* achange.output         = OUTPUT_1         */
       achange.treble         = AUDIO_IGNORE;
       achange.bass           = AUDIO_IGNORE;
       achange.pitch          = AUDIO_IGNORE;
       achange.monitor        = AUDIO_IGNORE;
       achange.dev_info       = (char *) NULL;
       acontrol.ioctl_request = AUDIO_CHANGE;
       acontrol.position      = 0;
       acontrol.request_info  = (char *) & achange;

/*     if ((mix = open(setupdata.mix_device, O_RDWR)) == -1) {
                g_warning("Can't open mixer-device %s\n", setupdata.mix_device);
                return -1;
               }*/
       if (ioctl (audio, AUDIO_CONTROL, & acontrol) == -1) {
               g_warning("Error writing mixer\n");
               return -1;
       }


       return audio;
}
#endif


#ifdef hpux
# ifndef hpux_alib

/* open the audio device on a HP9000 with HP-UX and set cd-quality */

gint open_hpux_audio(gchar *dev) {
gint audio;

	audio = open (dev, O_WRONLY | O_NONBLOCK);
	if (audio == -1) {
		/* error opening audio device */
		return -1;
	}

	if (ioctl(audio, AUDIO_SET_DATA_FORMAT, 
		AUDIO_FORMAT_LINEAR16BIT) == -1) {
		g_warning("Unable to set samplesize to 16 bit\n");
		return -1;
	}

	if (ioctl (audio, AUDIO_SET_CHANNELS, 2)==-1) {
		g_warning("Unable to set audio to stereo\n");
		return -1;
	}

	if (ioctl (audio, AUDIO_SET_SAMPLE_RATE, 44100) == -1) {
		g_warning("Unable to set audio to 44.1 kHz\n");
		return -1;
	}

	abuf_size = DEFAULT_BUFFER_SIZE;

	return audio;
}
# else

/* Some global variables for the hp-ux audio lib       */
/* Code mostly taken from simpleAudio.c sample of hpux */
static Audio *audioServer = (Audio *) NULL;
static struct protoent *tcpProtocolEntry;

gint open_hpux_audio(gchar *dev) {
int            streamSocket;
long            status;
SSPlayParams    playParams;
AGainEntry      gainEntry[4];
AudioAttrMask   AttribsMask;
AudioAttributes Attribs;
SStream         audioStream;
int             i;
ATransID        xid;

/* a_name specifies the audio controller name as a string.  */
/* We specify NULL, so the value of the AUDIO environment   */
/* variable is used.                                        */
char a_name = '\0';

        /* open audio connection */ 
        audioServer = AOpenAudio(&a_name, NULL);
        if (audioServer == NULL) {
		/* error opening audio device */
		g_warning("Unable to open audio device\n");
		return -1;
	}
	tcpProtocolEntry=getprotobyname("tcp");

	streamSocket = socket (AF_INET, SOCK_STREAM, 0);
	if( streamSocket < 0 ) {
		g_warning("Unable to request audio socket stream\n");
		return -1;
	}
	/* Set the default audio description */
	Attribs.type = ATSampled;
	Attribs.attr.sampled_attr.sampling_rate = 44100;
	Attribs.attr.sampled_attr.channels = 2;
	Attribs.attr.sampled_attr.data_format = ADFLin16;
	AttribsMask = ASSamplingRateMask | ASChannelsMask  | ASDataFormatMask;
	/* Use the transmit gain and the output channel defined */
	/* by the SPEAKER environment                           */
	gainEntry[0].u.o.out_ch = AOCTMono;
	gainEntry[0].gain = AUnityGain;
	gainEntry[0].u.o.out_dst = AODTDefaultOutput;
	playParams.gain_matrix.type = AGMTOutput;
	playParams.gain_matrix.num_entries = 1;
	playParams.gain_matrix.gain_entries = gainEntry;
	playParams.play_volume = AUnityGain;
	playParams.priority = APriorityNormal;
	playParams.event_mask = 0;

	/* Create an audio stream */
	xid = APlaySStream( audioServer, AttribsMask, &Attribs, 
			    &playParams, &audioStream, NULL);

	/* Connect the stream socket to the audio stream port */
	status = connect( streamSocket, (struct sockaddr *)&audioStream.tcp_sockaddr,
			  sizeof(struct sockaddr_in));
	if (status < 0) {
	  printf ("Errno: %d\n", errno);
		g_warning ("Connection of an audio stream socket failed\n");
	/*	close (streamSocket);*/
		return -1;
	}

	/* Tell TCP to not delay sending data written to this socket */
	i = -1;
	setsockopt( streamSocket, tcpProtocolEntry->p_proto, TCP_NODELAY,
		    &i, sizeof(i) );

	abuf_size = DEFAULT_BUFFER_SIZE;
	return (streamSocket);
}

# endif
#endif


#if defined(__sgi)

static ALport  irixAudioPort = NULL;   /* Our interface to SGI audio device */
static int     irixAudioRate = 44100;  /* The samples rate */

gint open_irix_audio(gchar *devName) {

	ALvalue		v [1];
	ALpv		pv [2], q [1];
	ALconfig	audioConfig;
	int		dev;

	if (irixAudioPort != NULL)      /* The device is already open. */
		return 0;

	/* Find the selected audio device */
	if (devName[0] == '\0') {
		dev = AL_DEFAULT_OUTPUT;
	}
	else {
		q[0].param = AL_LABEL;
		q[0].value.ptr = devName;
		q[0].sizeIn = strlen(devName) + 1;
		if (alQueryValues(AL_SYSTEM, AL_DEVICES, v, 1, q, 1) <= 0) {
			g_warning("Invalid audio port name\n");
			return -1;
		}
		dev = v[0].i;
	}

	/* Allocate a config structure */
	audioConfig = alNewConfig();

	/* Set the audio device */
	if (alSetDevice(audioConfig, dev) < 0) {
		g_warning("Unable to set the audio device\n");
		return -1;
	}

	/* Set the buffer size to contain 1/2 second of samples */
	abuf_size = irixAudioRate;
	if (alSetQueueSize(audioConfig, abuf_size / 2) < 0) {
		g_warning("Unable to set queue size\n");
		return -1;
	}

	/* Set the samples width */
	if (alSetWidth(audioConfig, AL_SAMPLE_16) < 0) {
		g_warning("Unable to set sample size to 16 bit\n");
		return -1;
	}

	/* Set the number of channels */
	if (alSetChannels(audioConfig, AL_STEREO) < 0) {
		g_warning("Unable to set audio to stereo\n");
		return -1;
	}

	/* Open the audio port */
	if ((irixAudioPort = alOpenPort("wavplay", "w", audioConfig) ) == NULL) {
		g_warning("Unable to open audio port\n");
		return -1;
	}

	/* Set the samples rate */
	pv[0].param = AL_RATE;
	pv[0].value.ll = alDoubleToFixed((double) irixAudioRate);
	if (alSetParams(dev, pv, 1) < 0 ) {
		g_warning("Unable to set audio to 44.1 kHz\n");
		return -1;
	}

	return 0;
}

void close_irix_audio() {

	if (irixAudioPort != NULL) {
		(void) alClosePort(irixAudioPort);
		irixAudioPort = NULL;
	}
}

void write_irix_audio(void *buffer, int bufferSize) {

	int	frames = bufferSize / 4;

	while (alGetFillable(irixAudioPort) < frames) {
		usleep(100);
	}
	if (alWriteFrames(irixAudioPort, buffer, frames) < 0) {
		g_warning("Error while writing to the audio port\n");
	}
}

#endif


/* print usage info */

void usage(gchar *cmd) {

	g_print("Usage: %s [options] filename  (Version: %s)\n", cmd, XCDROAST_VERSION);
	g_print("Options:\n");
	g_print("	-d audio-device\n");
	g_print("	-g : gui-mode\n");
	g_print("	-q : quiet-mode\n");
}


/* main programm */

gint main(gint argc, gchar **argv) {
gint c;
gchar audio_dev[MAXLINE];
gchar wavname[MAXLINE];
gint audio = -1;
gint fd;
gint l;
gint guimode = 0;
gint quiet = 0;
gint doplay = 1;
gint tick = 0;
gint oldtick = 0;
off_t bytessofar = 0;
off_t totalbytes;
gint min,sec;
gchar keybuffer[MAXLINE];
#if !(defined(linux))
 gint ii;
 guchar tmpswap;
#endif
	/* if no device given, set default device */
	strcpy(audio_dev,DEFAULT_AUDIO_DEVICE);
	strcpy(wavname,"");

	while ((c = getopt(argc, argv, "d:gq")) != EOF) 
	switch ((gchar)c) {
	
	case 'd':
		strncpy(audio_dev,optarg,MAXLINE);
		break;
	
	case 'g':
		guimode = 1;
		quiet = 1;
		break;
	
	case 'q':
		quiet = 1;
		break;
	
	default:
		usage(argv[0]);
		exit(1);
	}
	
	/* additional parameter given? */
	if (optind > argc -1) {
		usage(argv[0]);
		exit(1);
	} else {
		strncpy(wavname,argv[optind++],MAXLINE);
	}

	/* the open sets also the global abuf_size-variable */

#if defined(linux) || defined(__FreeBSD__)
	audio = open_linux_audio(audio_dev);
#endif
#if defined(sun) || defined(__OpenBSD__)
	audio = open_solaris_audio(audio_dev);
#endif
#ifdef aix
	audio = open_aix_audio(audio_dev);
#endif
#ifdef hpux
	audio = open_hpux_audio(audio_dev);
#endif
#if (defined(__MACH__) && defined(__APPLE__))
	audio = open_macosx_audio();
#endif
#if defined(__sgi)
	audio = open_irix_audio(audio_dev);
#endif

	if (audio < 0) {
		g_warning("Can't init sound-system\n");
		exit(1); 
	}

	if ((audiobuf = (guchar *)g_new(guchar *,abuf_size)) == NULL) {
		g_warning("Unable to allocate audio-buffer\n");
		exit(1);
	}

	/* open wavfile */
	fd = open (wavname, O_RDONLY, 0);

	if (fd == -1) {
#if !(defined(__MACH__) && defined(__APPLE__))
		close(audio);
#endif
		g_warning("Can't open wav-file\n");
		exit(1);
	}

	/* get filesize */
	totalbytes = is_std_wav_file(fd, &waveBase) ;

	/* is it a wav-file? */
	if (totalbytes == 0) {
		g_warning("No valid wavfile, or not in cd-quality\n");
		exit(0);
	}

	if (!quiet) {
		min = (gint) (totalbytes/(CDDAFRAME*75*60));
		sec = (gint) ((totalbytes/CDDAFRAME) % (60*75))/75;
		g_print("Playing: %s [%d:%02d]\n", wavname,
			min,sec);	
	}

	if (guimode) {
		/* in gui-mode we communicate via stdin and stdout */
		/* stdin must be nonblocking */
		fcntl(STDIN_FILENO,F_SETFL,O_NONBLOCK);
		
		/* update display */
		g_print("secs%d\n",(gint) (totalbytes/CDDAFRAME)/75);
		fflush(stdout);
	}

	/* ok..all set now, start playing */
	while (1) {

		/* guimode: look for commands from stdin */
		if (guimode && (read_line(STDIN_FILENO,keybuffer,MAXLINE) > 0)) {
			/* stop command */
			if (g_strncasecmp(keybuffer,"stop",4) == 0) {
				lseek(fd, waveBase, SEEK_SET);
				bytessofar = 0;
				tick = 0;
				doplay = 0;
				g_print("stop0\n");
				fflush(stdout);
			}

			/* play command */
			if (g_strncasecmp(keybuffer,"play",4) == 0) {
				doplay = 1;
			}	

			/* pause command */
			if (g_strncasecmp(keybuffer,"pause",5) == 0) {
				doplay=1-doplay;
				g_print("%s%d\n",doplay?"play":"stop",tick);
				fflush(stdout);
			}
	
			/* setxxx command */
			if (g_strncasecmp(keybuffer,"set",3) == 0) {
				tick = atoi(keybuffer+3);
				bytessofar = (off_t)tick *CDDAFRAME*75; 
				lseek(fd, waveBase+bytessofar,
					SEEK_SET);
				g_print("%s%d\n",doplay?"play":"stop",tick);
				fflush(stdout);
			}

			/* quit command */
			if (g_strncasecmp(keybuffer,"quit",4) == 0) {
				break;
			}
		}

		/* we are in play mode right now */
		if (doplay) {	

			/* read from wav-file */
			/* but only up until we reach totalbytes */
			l = totalbytes - bytessofar ;
			if (l > abuf_size)
				l = abuf_size ;
			if (l < 0)
				l = 0 ;

			if (l > 0)
				l = read(fd, audiobuf, l);
			if (l > 0) {
#if !(defined(linux))
			/* turn byte order only on non linux platforms */
			/* linux on spark wont work with that */
				if (G_BYTE_ORDER == G_BIG_ENDIAN) { 
					/* turn endian-ness */
					for (ii=0; ii<l; ii+=2) {
						tmpswap=audiobuf[ii];
						audiobuf[ii]=audiobuf[ii+1];
						audiobuf[ii+1]=tmpswap;
					}
				}
#endif
				if (guimode) {
					/* at which second are we right now? */
					tick=(gint)(bytessofar/CDDAFRAME/75);
					if (tick != oldtick) {
						g_print("play%d\n",tick);
						fflush(stdout);
						oldtick = tick;
					}
				}

#if (defined(__MACH__) && defined(__APPLE__))
				audio_write(audiobuf,l);
#elif defined(__sgi)
				write_irix_audio(audiobuf, l);
#else
				if (write(audio, audiobuf, l) != l) {
					g_print("write error to audio-device\n");
					exit(-1);
				}
#endif
				bytessofar+=(off_t)l;

			} else {
				/* read error on wav-file */
				if (l == -1) {
					g_print("read error on wav-file\n");
					exit(-1);
				}
				
				/* EOF reached */
				doplay = 0;
				if (guimode) {
					/* roll back */
					lseek(fd, waveBase, SEEK_SET);
					bytessofar = 0;
					tick = 0;
					g_print("done%d\n",tick);
					fflush(stdout);
				} else { 
					break;
				}
			}
		} else {
			/* we are in pause-mode */
			/* wait a short while and then look for commands again */
			usleep(100);
		}
	}

	close(fd);

#if (defined(__MACH__) && defined(__APPLE__))
	audio_close();
#elif defined(__sgi)
	close_irix_audio();
#else
	close(audio);
#endif

#if (defined hpux) && (defined hpux_alib)
	ASetCloseDownMode( audioServer, AKeepTransactions, NULL );
	ACloseAudio( audioServer, NULL );
#endif

	return 0;
}
