#include <../source/TAL/objbase.h>
#include <../source/vfs/iobase.h>
#include <../source/PAL/oshead.h>

#ifdef	__WIN32__

/* Number of buffers in queue */
#define	NUM_BUFFERS		4

typedef struct waveout_port_s
{
	ioport_base_t	vtbl;
	HANDLE			completion, wake_event;
	CRITICAL_SECTION lock;
	HWAVEOUT		dev;
	MMTIME 			mmt;
	u32_t			num_channels;
	u32_t			bps;
	u32_t			out_burst;	/* size of DMA chunk */
	u32_t			next;		/* number of block to write data to */
	u32_t			wrote;		/* number of waveOutWrite()'d samples */
	vboolean		paused;
	vboolean		reset;
	vboolean		break_loop;		/* == true on destroy() */
	WAVEHDR			*headers[NUM_BUFFERS];
	LPSTR			dmabuf;		/* contiguous output buffer */
	u8_fifo_t		data;
    vhandle_t		out_thr;
}	waveout_port_t, *waveout_port_p;

/* Implemented in PAL/wavein.c */
void	V_CALL	mmsInitHeader(WAVEFORMATEX *wfe,
	u32_t num_channels,u32_t sampling_frequency,u32_t bits_per_sample);

#ifdef	CHECKED_BUILD
/* Implemented in PAL/wavein.c */
extern	char_p	mmsError(MMRESULT res);
#endif	/* CHECKED_BUILD */			

void	V_CALL
waveout_thread(void *arg)
{
	WAVEHDR *wh;
	vsize_t i, buf_len, available;
	waveout_port_p self = (waveout_port_p)arg;
    for(;;)
    {
    	ResetEvent(self->wake_event);
	    WaitForSingleObject(self->wake_event,10);
    	EnterCriticalSection(&self->lock);
		if(self->break_loop)
		{
			LeaveCriticalSection(&self->lock);
			threadExit(0);
		}
		if(self->reset)
		{
			u8fPurge(&self->data);
			waveOutReset(self->dev);
			self->wrote = 0;
			waveOutRestart(self->dev);
			self->reset = false;
			self->paused = false;
		}
    	if(self->paused)
		{
			LeaveCriticalSection(&self->lock);
			continue;
		}
        /*
         * Count available buffers
         */
        for(available=0,i = self->next;available<NUM_BUFFERS;available++)
        {
			if(self->headers[i]->dwFlags & WHDR_DONE)
				i = (i+1) % NUM_BUFFERS;
			else
            	break;
		}
		while(available)
		{
    		buf_len = u8fCount(&self->data);
        	if(buf_len > self->out_burst)
				buf_len = self->out_burst;
			else
				if(buf_len < self->out_burst && (!buf_len || available >= (NUM_BUFFERS-1)))
	            	break;
			wh = self->headers[self->next];
			self->next = (self->next+1) % NUM_BUFFERS;
			u8fPopBuffer(&self->data,(u8_t*)wh->lpData,buf_len);
			wh->dwBufferLength = buf_len;
			wh->dwFlags &= ~(DWORD)WHDR_DONE;
			ResetEvent(self->completion);
			waveOutWrite(self->dev,wh,sizeof(WAVEHDR));
			self->wrote += buf_len / (self->bps >> 3);
            available--;
		}
       	if(available == NUM_BUFFERS && !u8fCount(&self->data))
			SetEvent(self->completion);
		LeaveCriticalSection(&self->lock);
	}
}

V_LOCAL	vsize_t V_CALL
waveout_write(vhandle_t handle,void *buffer,vsize_t len)
{
	waveout_port_p self=(waveout_port_p)handle;
	EnterCriticalSection(&self->lock);
	if(len)
    	u8fPushBuffer(&self->data,(u8_t*)buffer,len);
	LeaveCriticalSection(&self->lock);
	SetEvent(self->wake_event);
	return len;
}

V_LOCAL	int V_CALL
waveout_control(vhandle_t handle,unsigned code,void *data)
{
	DWORD s;
	u32_t milliseconds;
	int retcode = -1;
	waveout_port_p self=(waveout_port_p)handle;
	switch(code)
	{
		case pctl_waveout_wait:
			if(self->paused)
				return -1;
			milliseconds = (data) ? *((u32_t*)data) : INFINITE;
			SetEvent(self->wake_event);
			s = WaitForSingleObject(self->completion,milliseconds);
			retcode = (s == WAIT_OBJECT_0) ? 0 : -1;
			break;
		case pctl_waveout_get_pending_samples:
			/* compute number of non-written samples (not bytes !) */
			if(!data)
				break;
			self->mmt.u.sample=0;
			self->mmt.wType = TIME_SAMPLES;
			if(waveOutGetPosition(self->dev,&self->mmt,sizeof(MMTIME)) != MMSYSERR_NOERROR || self->mmt.wType != TIME_SAMPLES)
			{
				*((u32_t*)data) = 0;
				break;
			}
			*((u32_t*)data) = self->wrote - (self->mmt.u.sample * self->num_channels);
			*((u32_t*)data) += u8fCount(&self->data) / (self->bps >> 3);
			retcode = 0;
			break;
		case pctl_waveout_pause:
			EnterCriticalSection(&self->lock);
			if(!self->paused && waveOutPause(self->dev) == MMSYSERR_NOERROR)
			{
				self->paused = true;
				retcode = 0;
			}
			LeaveCriticalSection(&self->lock);
			break;
		case pctl_waveout_resume:
			EnterCriticalSection(&self->lock);
			if(self->paused && waveOutRestart(self->dev)==MMSYSERR_NOERROR)
			{
				self->paused = false;
				retcode = 0;
			}
			LeaveCriticalSection(&self->lock);
			break;
		case pctl_waveout_reset:
			EnterCriticalSection(&self->lock);
			self->reset = true;
			SetEvent(self->wake_event);
			LeaveCriticalSection(&self->lock);
			retcode = 0;
			break;
		default:
			break;
	}
	return retcode;
}

V_LOCAL u32_t V_CALL
waveout_destroy(vhandle_t handle)			
{
	int i;
	waveout_port_p self=(waveout_port_p)handle;
	if(--self->vtbl.base.ref_count)
		return self->vtbl.base.ref_count;
	waveout_control(handle,pctl_waveout_reset,NULL);
	self->break_loop = true;
	SetEvent(self->wake_event);
    threadJoin(self->out_thr,NULL);
	for(i=0;i<NUM_BUFFERS;i++)
    	waveOutUnprepareHeader(self->dev,self->headers[i],sizeof(WAVEHDR));
   	memFree(self->headers[0]);
   	memFree(self->dmabuf);
   	u8fClear(&self->data);
    waveOutClose(self->dev);
	CloseHandle(self->completion);
	CloseHandle(self->wake_event);
	DeleteCriticalSection(&self->lock);
	memFreeObject(self);
	return 0;
}

V_LOCAL vhandle_t
winmmPlaybackOpen(u32_t num_channels,u32_t sampling_frequency,
	u32_t bits_per_sample)
{
	int i;
	MMRESULT rc;
	HWAVEOUT dev;
	WAVEFORMATEX fmt;
	waveout_port_p self;
	mmsInitHeader(&fmt,num_channels,sampling_frequency,bits_per_sample);
	self=(waveout_port_p)ioAllocPort(sizeof(waveout_port_t));
	InitializeCriticalSection(&self->lock);
	self->wake_event = CreateEvent(NULL,TRUE,FALSE,NULL);
	EnterCriticalSection(&self->lock);
	rc = waveOutOpen(&dev,WAVE_MAPPER,&fmt,(DWORD)self->wake_event,
		(DWORD)self,/* CALLBACK_EVENT */ CALLBACK_NULL);
	if(rc!=MMSYSERR_NOERROR)
	{
		LeaveCriticalSection(&self->lock);
		DeleteCriticalSection(&self->lock);
		__libvCloseHandle(self);
#ifdef	CHECKED_BUIL
		conlPrintf(LOG_ERROR,
			"wavePlaybackOpen(%u,%u,%u): waveOutOpen() failed: '%s'.\n",
			num_channels,sampling_frequency,bits_per_sample,
			mmsError(rc));
#endif	/* CHECKED_BUIL */
		return NULL;
	}
	self->vtbl.base.destroy = waveout_destroy;
	self->vtbl.write = waveout_write;
	self->vtbl.ioctl = waveout_control;
	self->completion = CreateEvent(NULL,TRUE,TRUE,NULL);
	self->dev=dev;
	self->bps=bits_per_sample;
	self->num_channels = num_channels;
	self->break_loop = false;
	self->paused = false;
	self->reset = false;
	self->next = 0;
	u8fInit(&self->data);
	self->out_burst = sampling_frequency * (bits_per_sample>>3) * num_channels;
	memZero(&self->mmt,sizeof(MMTIME));
	self->mmt.wType=TIME_SAMPLES;
	if(self->out_burst < 2048)
		self->out_burst = 2048;
    self->dmabuf=(LPSTR)memAlloc(self->out_burst * NUM_BUFFERS);
	self->headers[0] = (WAVEHDR*)memAlloc(sizeof(WAVEHDR) * NUM_BUFFERS);
	for(i=0;i<NUM_BUFFERS;i++)
	{
		self->headers[i] = self->headers[0] + i;
		memZero(self->headers[i],sizeof(WAVEHDR));
		self->headers[i]->lpData = self->dmabuf + self->out_burst * i;
		self->headers[i]->dwBufferLength = self->out_burst;
		memZero(self->headers[i]->lpData,self->out_burst);
		waveOutPrepareHeader(self->dev,self->headers[i],sizeof(WAVEHDR));
        self->headers[i]->dwFlags |= WHDR_DONE;
	}
	LeaveCriticalSection(&self->lock);
    self->out_thr = threadCreate(waveout_thread,self);
	return self;
}

#elif	defined(__UNIX__) /* !__WIN32__ */

#ifdef	CONFIG_ESOUND

#include <esd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#if	defined(__linux) || defined(SOLARIS)
#ifdef	SOLARIS
#include <sys/conf.h>
#endif	/* SOLARIS */
#include <stropts.h>
#endif

V_LOCAL void
libv_esd_print_server_info(esd_server_info_t *info)
{
	conlPrintf(LOG_INFO,
		"EsounD server: %u Hz, %s bps, %s, in %s mode.\n",
		info->rate,
		((info->format & ESD_MASK_BITS) == ESD_BITS16) ? "16" :
			(((info->format & ESD_MASK_BITS) == ESD_BITS8) ? "8" : "???"),
		((info->format & ESD_MASK_CHAN) == ESD_STEREO) ? "stereo" : "mono",
		((info->format & ESD_MASK_MODE) == ESD_STREAM) ? "stream" :
			(((info->format & ESD_MASK_MODE) == ESD_SAMPLE) ? 
			"sample" : "unknown")
	);
}

V_LOCAL int
esd_open(int channels,int rate,int bps)
{
   	int esd, fn;
   	esd_server_info_t *info;
	esd_format_t format = ESD_STREAM | ESD_PLAY;
	esd = esd_open_sound(NULL);
   	if(esd >= 0)
   	{
		info = esd_get_server_info(esd);
		if(!rate)
			rate = info->rate;
		libv_esd_print_server_info(info);
		esd_free_server_info(info);
      	esd_close(esd);
    }
	else
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,
			"esd_open(): esd_open_sound() failed, no ESounD server.\n");
#endif	/* CHECKED_BUILD */
		return -1;
	}
	switch(bps)
	{
		case 16:
    		format |= ESD_BITS16;
			break;
		case 8:
    		format |= ESD_BITS8;
			break;
		default:
			return -1;
	}
	switch(channels)
	{
		case 2:
			format |= ESD_STEREO;
			break;
		case 1: 
			format |= ESD_MONO;
			break;
		default:
			return -1;
	}
	fn = esd_play_stream(format,rate,NULL,"libv_esd_out");
  	if(fn < 0)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"esd_open(): "
			"esd_play_stream() failed; probably invalid format.\n");
#endif	/* CHECKED_BUILD */
		return -1;
	}
	return fn;
}

#if	defined(NETBSD)
V_LOCAL void esd_flush(int fd)
{
	ioctl(fd, AUDIO_FLUSH, 0);
}
#elif defined(__linux) || defined(SOLARIS)
void  esd_flush(int fd)
{
	ioctl(fd, I_FLUSH, FLUSHRW);
}
#else	/* !SOLARIS && !__linux */
void  esd_flush(void)
{
}
#endif

typedef struct esdout_port_s
{
	ioport_base_t	vtbl;
	int				fd;
	int				samples_per_second;
	vhandle_t		timer;
	vec_t			pending_time;
	vec_t			fragment_time;
	u32_t			fragment_shift;
}	esdout_port_t, *esdout_port_p;

V_LOCAL int V_CALL
esdout_control(vhandle_t handle,int code,void *data)
{
	esdout_port_p self=(esdout_port_p)handle;
	switch(code)
	{
		case pctl_waveout_get_pending_samples:
			self->pending_time -= timerDiffTime(self->timer);
			if(self->pending_time < 0)
				self->pending_time = 0;
			*((u32_t*)data)=
				(u32_t)(self->pending_time * self->samples_per_second);
			return 0;
		case pctl_waveout_wait:
			self->pending_time -= timerDiffTime(self->timer);
			if(self->pending_time < 0)
				self->pending_time = 0;
			timeDelay((u32_t)(1000 * self->pending_time));
			return 0;
		case pctl_waveout_reset:
			esd_flush(self->fd);
			return 0;
		case pctl_waveout_pause:
			return -1;
		case pctl_waveout_resume:
			return -1;
		default:
			return -1;
	}
}

V_LOCAL u32_t V_CALL
esdout_destroy(vhandle_t handle)
{
	esdout_port_p self=(esdout_port_p)handle;
	if(--self->vtbl.base.ref_count)
		return self->vtbl.base.ref_count;
	esdout_control(self,pctl_waveout_reset,NULL);
	close(self->fd);
	memFreeObject(self);
	return 0;
}	

V_LOCAL vsize_t V_CALL
esdout_write(vhandle_t handle,void *buffer,vsize_t len)
{
	esdout_port_p self=(esdout_port_p)handle;
	self->pending_time -= timerDiffTime(self->timer);
	if(self->pending_time < 0)
		self->pending_time = 0;
	self->pending_time += (vec_t)(len >> self->fragment_shift);
	return write(self->fd,buffer,len);
}	

V_LOCAL vhandle_t
esdPlaybackOpen(u32_t num_channels,u32_t sampling_frequency,
	u32_t bits_per_sample)
{
	int fd;
	esdout_port_p self;
	fd = esd_open(num_channels,sampling_frequency,bits_per_sample);
	if(fd == -1)
		return NULL;
	self=(esdout_port_p)ioAllocPort(sizeof(esdout_port_t));
	self->fd=fd;
	self->vtbl.base.destroy=esdout_destroy;
	self->vtbl.write=esdout_write;
	self->vtbl.ioctl=esdout_control;
	self->samples_per_second = sampling_frequency;
	self->fragment_shift=0;
	if(bits_per_sample==16)
		self->fragment_shift++;
	if(num_channels==2)
		self->fragment_shift++;
	self->fragment_time = (vec_t)1 / (vec_t)sampling_frequency;
	self->pending_time = 0;
	self->timer = timerCreate();
	timerDiffTime(self->timer); /* reset timer */
	return self;
}

#endif	/* CONFIG_ESOUND */

/*
 *	Bugs: we leave time control to OSS driver ...
 */
 
typedef struct waveout_port_s
{
	ioport_base_t	vtbl;
	int				fd;
	cond_t			wake;			/* set to activate writer */
	cond_t			completion;		/* set when no more data */
	mutex_t			lock;
	thread_t		writer_handle;
	u8_fifo_t		data;
	u32_t			outburst;
	u32_t			cksize;			/* == outburst * bps/8 */
	u8_t			*dmabuf;
	void			(*clear_buffer)(struct waveout_port_s *);
	vboolean		pause;
	vboolean		destroy;
	u32_t			bps, channels;
}	waveout_port_t, *waveout_port_p;

V_LOCAL int V_CALL
waveout_control(vhandle_t handle,unsigned code,void *data)
{
	int arg;
	waveout_port_p self=(waveout_port_p)handle;
	switch(code)
	{
		case pctl_waveout_get_pending_samples:
			mutexLock(self->lock);
			ioctl(self->fd,SNDCTL_DSP_GETODELAY,&arg);
			/*	EXPLAINME: why OSS driver returns ANY value in
			 *	range [0...outburst] if no more sound data ? */
			if(arg < self->outburst)
				arg=0;
			*((u32_t*)data) = (u8fCount(&self->data) + (u32_t)arg) / 
				(self->bps >> 3);
			mutexUnlock(self->lock);
			return 0;
		case pctl_waveout_wait:
			condSet(self->wake);
			if(data)
				condTimedWait(self->completion,*((u32_t*)data));
			else
				condWait(self->completion);
			ioctl(self->fd,SNDCTL_DSP_SYNC,NULL);
			return 0;
		case pctl_waveout_reset:
			mutexLock(self->lock);
			u8fPurge(&self->data);
			ioctl(self->fd,SNDCTL_DSP_RESET);
			mutexUnlock(self->lock);
			condSet(self->wake);
			return 0;
		case pctl_waveout_pause:
			self->pause=true;
			return 0;
		case pctl_waveout_resume:
			self->pause=false;
			condSet(self->wake);
			return 0;
		default:
			return -1;
	}
}

V_LOCAL u32_t V_CALL
waveout_destroy(vhandle_t handle)
{
	waveout_port_p self=(waveout_port_p)handle;
	if(--self->vtbl.base.ref_count)
		return self->vtbl.base.ref_count;
	self->destroy=true;
	waveout_control(self,pctl_waveout_reset,NULL);
	waveout_control(self,pctl_waveout_wait,NULL);
	condSet(self->wake);
	threadJoin(self->writer_handle,NULL);
	mutexLock(self->lock);
	u8fClear(&self->data);
	memFree(self->dmabuf);
	close(self->fd);
	condDestroy(self->wake);
	condDestroy(self->completion);
	mutexUnlock(self->lock);
	mutexDestroy(self->lock);
	memFreeObject(self);
	return 0;
}	

V_LOCAL vsize_t V_CALL
waveout_write(vhandle_t handle,void *buffer,vsize_t len)
{
	waveout_port_p self=(waveout_port_p)handle;
	mutexLock(self->lock);
	u8fPushBuffer(&self->data,(u8_t*)buffer,len);
	condReset(self->completion);
	if(u8fCount(&self->data) < (self->cksize << 1))
	{
		/* still not enought data ! */
		mutexUnlock(self->lock);
		return len;
	}
	condSet(self->wake);
	mutexUnlock(self->lock);
	return len;
}	

V_LOCAL	void waveout_clear_buffer_16(waveout_port_t *self)
{
	memZero(self->dmabuf,self->cksize);
}

V_LOCAL	void waveout_clear_buffer_8(waveout_port_t *self)
{
	memFill(self->dmabuf,0x80,self->cksize);
}

V_LOCAL void V_CALL
waveout_thread(void *arg)
{
	vsize_t rest, avail;
	waveout_port_p self;
	self=(waveout_port_p)arg;
	for(;;)
	{
		condWait(self->wake);
		if(self->destroy)
			threadExit(0);
		for(;;)
		{
			if(self->pause)
			{
				int arg=-1;
				condReset(self->wake);
				ioctl(self->fd,PCM_ENABLE_OUTPUT,&arg);
				condWait(self->wake);
				arg=1;
				ioctl(self->fd,PCM_ENABLE_OUTPUT,&arg);
			}
			mutexLock(self->lock);
			rest=u8fCount(&self->data);
			if(rest < self->cksize)
			{
				avail=rest;
				self->clear_buffer(self);
				if(!rest)
				{
					condReset(self->wake);
					condSet(self->completion);
					mutexUnlock(self->lock);
					break;
				}
			}
			else
				avail=self->cksize;
			u8fPopBuffer(&self->data,self->dmabuf,avail);
			mutexUnlock(self->lock);
			write(self->fd,self->dmabuf,self->cksize);
		}
	}
}

V_LOCAL vhandle_t
ossPlaybackOpen(u32_t num_channels,u32_t sampling_frequency,
	u32_t bits_per_sample)
{
	int fd;
	int arg;
	int outburst;
	waveout_port_p self;
	switch(bits_per_sample)
	{
		case 16: arg=AFMT_S16_LE; break;
		case 8: arg=AFMT_U8; break;
		default:
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"wavePlaybackOpen(%u,%u,%u) unsupported BPS.\n",
				num_channels,sampling_frequency,bits_per_sample);
#endif	/* CHECKED_BUILD */
			return NULL;
	}
	fd = open("/dev/dsp",O_WRONLY | O_SYNC,00200);
	if(!fd)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"wavePlaybackOpen(%u,%u,%u) unable to open /dev/dsp.",
			num_channels,sampling_frequency,bits_per_sample);
#endif	/* CHECKED_BUILD */
		return NULL;
	}
	ioctl(fd,SNDCTL_DSP_RESET);
	if(ioctl(fd,SNDCTL_DSP_GETBLKSIZE,&outburst)==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"wavePlaybackOpen(%u,%u,%u) unable to get dma buffer size.\n",
			num_channels,sampling_frequency,bits_per_sample);
#endif	/* CHECKED_BUILD */
		close(fd);
		return NULL;
	}
	if(ioctl(fd,SNDCTL_DSP_SETFMT,&arg)==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"wavePlaybackOpen(%u,%u,%u) unable to set BPS.\n",
			num_channels,sampling_frequency,bits_per_sample);
#endif	/* CHECKED_BUILD */
		close(fd);
		return NULL;
	}
	arg=(int)sampling_frequency;
	if(ioctl(fd,SNDCTL_DSP_SPEED,&arg)==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"wavePlaybackOpen(%u,%u,%u) unable to set rate.\n",
			num_channels,sampling_frequency,bits_per_sample);
#endif	/* CHECKED_BUILD */
		close(fd);
		return NULL;
	}
	if(num_channels==2)
		arg = 1;
	else
		arg = 0;
    if(ioctl(fd,SNDCTL_DSP_STEREO,&arg)==-1)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"wavePlaybackOpen(%u,%u,%u) unable to change to %s.\n",
			num_channels,sampling_frequency,bits_per_sample,
			arg ? "mono" : "stereo");
#endif	/* CHECKED_BUILD */
		close(fd);
		return NULL;
	}
	self=(waveout_port_p)ioAllocPort(sizeof(waveout_port_t));
	self->fd=fd;
	self->outburst=outburst;
	self->vtbl.base.destroy=waveout_destroy;
	self->vtbl.write=waveout_write;
	self->vtbl.ioctl=waveout_control;
	self->bps=bits_per_sample;
	if(self->bps==16)
		self->clear_buffer=waveout_clear_buffer_16;
	else
		self->clear_buffer=waveout_clear_buffer_8;
	self->channels=num_channels;
	self->wake=condCreate(false);
	condReset(self->wake);
	self->completion=condCreate(false);
	condSet(self->completion);
	self->destroy=false;
	self->pause=false;
	self->lock=mutexCreate();
	self->cksize=self->outburst * self->channels * (self->bps >> 3);
	self->dmabuf=(u8_t*)memAlloc(self->cksize);
	u8fInit(&self->data);
	self->writer_handle=threadCreate(waveout_thread,self);
	return self;
}

#endif	/* __UNIX__ */

vboolean	V_GLOBAL
wavePlaybackPause(vhandle_t dev)
{
	return fileControl(dev,pctl_waveout_pause,NULL);
}

vboolean	V_GLOBAL
wavePlaybackResume(vhandle_t dev)
{
	return fileControl(dev,pctl_waveout_resume,NULL);
}

vboolean	V_GLOBAL
wavePlaybackReset(vhandle_t dev)
{
	return fileControl(dev,pctl_waveout_reset,NULL);
}

vboolean	V_GLOBAL
wavePlaybackWait(vhandle_t dev,u32_t milliseconds)
{
	return fileControl(dev,pctl_waveout_wait,
		milliseconds == MAXLONG ? NULL : &milliseconds);
}

vhandle_t	V_GLOBAL
wavePlaybackOpen(u32_t num_channels,u32_t sampling_frequency,
	u32_t bits_per_sample)
{
	vhandle_t wo;
#ifdef	__WIN32__
	wo = winmmPlaybackOpen(num_channels,sampling_frequency,bits_per_sample);
#elif	defined(__UNIX__)
#ifdef	CONFIG_ESOUND
	wo = esdPlaybackOpen(num_channels,sampling_frequency,bits_per_sample);
	if(wo)
		return wo;
#endif	/* CONFIG_ESOUND */
	wo = ossPlaybackOpen(num_channels,sampling_frequency,bits_per_sample);
#endif	/* __UNIX__ */
	return wo;
}
