/*
 *	wavplay.c
 *
 *	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.
 *	30.05.99 tn
 *  07.11.18 tn - add ALSA support for linux
 *
 *
 *  Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
 *
 *  This file is part of xcdroast.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#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)
# include <alsa/asoundlib.h>
#endif
#if 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(__sgi)
#include <dmedia/audio.h>
#endif

static off_t waveBase ;
static gint abuf_size;
#if defined(linux) 
static gint alsa_frame_size;
#endif
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) 
#define DEFAULT_AUDIO_DEVICE "default"
#elif 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(linux) 

/*
 * open the alsa-device of linux and set cd-quality
 */
snd_pcm_t *open_alsa_audio(gchar *dev) {
snd_pcm_t *playback_handle;
snd_pcm_hw_params_t *hw_params;
snd_pcm_uframes_t buffer_size, periods;
gint err;

	if ((err = snd_pcm_open (&playback_handle, dev, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
			g_warning("cannot open audio device %s (%s)\n", dev, snd_strerror (err));
			return(NULL);
	}
	if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
			g_warning("cannot allocate hardware parameter structure (%s)\n",
				 snd_strerror (err));
			return(NULL);
	}
				 
	if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < 0) {
			g_warning("cannot initialize hardware parameter structure (%s)\n",
				 snd_strerror (err));
			return(NULL);
	}
	
	if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
			g_warning("cannot set access type (%s)\n", snd_strerror (err));
			return(NULL);
	}
	
	if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
			g_warning("cannot set sample format (%s)\n", snd_strerror (err));
			return(NULL);
	}
	
	if ((err = snd_pcm_hw_params_set_rate (playback_handle, hw_params, 44100, 0)) < 0) {
			g_warning("cannot set sample rate (%s)\n", snd_strerror (err));
			return(NULL);
	}
	
	if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2)) < 0) {
			g_warning("cannot set channel count (%s)\n", snd_strerror (err));
			return(NULL);
	}

    periods = 4;
	if ((err = snd_pcm_hw_params_set_periods (playback_handle, hw_params, periods, 0)) < 0) {
			g_warning("cannot set periode size (%s)\n", snd_strerror (err));
			return(NULL);
	}

    buffer_size = CDDAFRAME * periods;
	if ((err = snd_pcm_hw_params_set_buffer_size_near (playback_handle, hw_params, &buffer_size)) < 0) {
			g_warning("cannot set buffer size (%s)\n", snd_strerror (err));
			return(NULL);
	}
	
	if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < 0) {
			g_warning("cannot set parameters (%s)\n", snd_strerror (err));
			return(NULL);
	}
	
    /* determine real buffer size */
    snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
    alsa_frame_size = snd_pcm_format_size(SND_PCM_FORMAT_S16_LE, 2);
    abuf_size = (gint)buffer_size * alsa_frame_size;

	snd_pcm_hw_params_free (hw_params);

	if ((err = snd_pcm_prepare (playback_handle)) < 0) {
			g_warning("cannot prepare audio interface for use (%s)\n",
				 snd_strerror (err));
			return(NULL);
		}

	return (playback_handle);
}

#endif

#if 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 only to external speaker                */
	/* else the output device will be set to the predefined   */
	/* 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 (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 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 audio = -1;
 gint ii;
 guchar tmpswap;
#endif
#if defined(linux)
 snd_pcm_t *playback_handle;
 snd_pcm_sframes_t frames, wroteframes;
#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) 
	playback_handle = open_alsa_audio(audio_dev);
#endif
#if 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(__sgi)
	audio = open_irix_audio(audio_dev);
#endif

#if defined(linux)
	if (playback_handle == NULL) {
		g_warning("Can't init sound-system\n");
		exit(1); 
	}
#endif
#if !(defined(linux))
	if (audio < 0) {
		g_warning("Can't init sound-system\n");
		exit(1); 
	}
#endif

	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) {
		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_ascii_strncasecmp(keybuffer,"stop",4) == 0) {
				lseek(fd, waveBase, SEEK_SET);
#if defined(linux)
                snd_pcm_drain(playback_handle);
#endif
				bytessofar = 0;
				tick = 0;
				doplay = 0;
				g_print("stop0\n");
				fflush(stdout);
			}

			/* play command */
			if (g_ascii_strncasecmp(keybuffer,"play",4) == 0) {
#if defined(linux)
                snd_pcm_prepare(playback_handle);
#endif
				doplay = 1;
			}	

			/* pause command */
			if (g_ascii_strncasecmp(keybuffer,"pause",5) == 0) {
                if (doplay) {
				    doplay = 0;
				    g_print("%s%d\n","stop",tick);
				    fflush(stdout);
                }
			}
	
			/* setxxx command */
			if (g_ascii_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_ascii_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 won't 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(__sgi)
				write_irix_audio(audiobuf, l);
#elif defined(linux)
                frames = (snd_pcm_sframes_t) (l/alsa_frame_size);
				wroteframes = snd_pcm_writei (playback_handle, audiobuf, frames);
				if (wroteframes != frames) {
					g_print("write error to audio-device (wrote %d/%d)\n", (gint)wroteframes, (gint)frames);
					exit(-1);
				}
#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(__sgi)
	close_irix_audio();
#elif defined(linux)
	snd_pcm_close (playback_handle);
#else
	close(audio);
#endif

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

	return 0;
}
