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

#ifdef	CONFIG_AUDIO_MP3
#include <../support/mp3lib/src/mp3.h>
#endif	/* CONFIG_AUDIO_MP3 */

#ifndef	WAVE_FORMAT_PCM
#define	WAVE_FORMAT_PCM		1
#endif	/* WAVE_FORMAT_PCM */

#ifdef	_MSC_VER
#pragma warning(disable:4103)
#endif	/* _MSC_VER */

/*
 *	Microsoft WAV format description
 */
#include <../source/PAL/pack0.h>
typedef struct	wave_format_s
{
	u16_t	format_tag;			/*	only WAVE_FORMAT_PCM supported */
	u16_t	num_channels;		/*	must be either 1 or 2 */
	u32_t	sampling_frequency;
	u32_t	bytes_per_second;	/*	== samples_per_second * block_align */
	u16_t	block_align;		/*	== num_channels*bits_per_sample / 8 */
	u16_t	bits_per_sample;
}	ATTRIBUTE_PACKED wave_format_t, *wave_format_p;
#include <../source/PAL/ppack.h>

#ifdef	_MSC_VER
#pragma warning(default:4103)
#endif	/* _MSC_VER */

/*
 *	This object represents uncompressed PCM wave file.
 */
typedef struct	wave_data_s
{
	ioport_base_t	port;
	u16_t	num_channels;		/*	must be either 1 or 2 */
	u32_t	sampling_frequency;
	u32_t	bytes_per_second;	/*	== samples_per_second * block_align */
	u16_t	block_align;		/*	== num_channels*bits_per_sample / 8 */
	u16_t	bits_per_sample;	/* Bit rate */
	vsize_t	num_samples;		/* total number of samples (not bytes) */
	vsize_t	num_bytes;			/* total data size in bytes */
	vhandle_t df;				/* input file handle */
	vsize_t	transferred;		/* number of transferred bytes */
	vboolean writemode;			/* == true if self was wavOpenWrite()'d */
	vboolean loop;				/* seek to start after last read() */
	vsize_t	data_offset;		/* offset to wave data in self->df */
	vsize_t	tail_garbage_length;
}	wave_data_t, *wave_data_p;

typedef struct chunk_head_s
{
	char	id[4];		/* identifier: "RIFF" for file header */
	u32_t	len;		/* remaining chunk length after header */
}	chunk_head_t, *chunk_head_p;

/*
 *	Reads header of the next chunk in RIFF file.
 *	Returns false if unexpected EOF reached.
 */
V_LOCAL vboolean
riff_read_head(vhandle_t in,chunk_head_t *head_ret)
{
	u32_t pos,rd;
	pos = fileTell(in);
	fileRead(in,head_ret->id,4);
	head_ret->len = fileGetLittleLong(in);
	rd = fileTell(in) - pos;
	if(rd != 8)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"riff_read_head(): unable to read chunk header.\n");
#endif	/* CHECKED_BUILD */
		return false;
	}
	return true;
}

/*
 *	Reads chunk and returns pointer to memAlloc()'ed block (or
 *	NULL if unexpected EOF reached).
 *	File pointer must be propely positioned (to first byte
 *	after chunk header).
 */
V_LOCAL void_p
riff_read_chunk_data(vhandle_t in,chunk_head_t *chead)
{
	void *cdata;
	cdata = memAlloc(chead->len);
	if(fileRead(in,cdata,chead->len) != chead->len)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"riff_read_chunk_data(): "
			"unexpected end of file in chunk '%c%c%c%c'.\n",
			chead->id[0],chead->id[1],chead->id[2],chead->id[3]);
#endif	/* CHECKED_BUILD */
		memFree(cdata);
		return NULL;
	}
	return cdata;
}

/*
 *	Reads wave format chunk.
 */
V_LOCAL	vboolean
riff_read_fmt(vhandle_t in,chunk_head_t *chead,wave_data_t *fmt)
{
	wave_format_t *cdata;
	vboolean ret = false;
	for(;;)
	{
		cdata = (wave_format_t*)riff_read_chunk_data(in,chead);
		if(!cdata)
			break;
		fmt->num_channels = LE2ME_SHORT(cdata->num_channels);
		fmt->bits_per_sample = LE2ME_SHORT(cdata->bits_per_sample);
		fmt->sampling_frequency = LE2ME_LONG(cdata->sampling_frequency);
#ifdef	CONFIG_WORKAROUND_ZERO_BPS
		if(!fmt->bits_per_sample)
		{
#ifdef	CHECKED_BUILD
			conlPuts(LOG_WARNING,"riff_read_fmt(): zero BPS, assuming 16.\n");
#endif	/* CHECKED_BUILD */
			fmt->bits_per_sample = 16;
		}
#endif	/* CONFIG_WORKAROUND_ZERO_BPS */
		if(!fmt->num_channels || !fmt->bits_per_sample || !fmt->sampling_frequency)
		{
#ifdef	CHECKED_BUILD
			conlPrintf(LOG_ERROR,"riff_read_fmt(): unexpected format: "
				"(nc %u, bps %u, freq %u).\n",fmt->num_channels,
				fmt->bits_per_sample,fmt->sampling_frequency);
#endif	/* CHECKED_BUILD */
			break;
		}
		fmt->block_align=
			(u16_t)(fmt->num_channels * (fmt->bits_per_sample >> 3));
		fmt->bytes_per_second=fmt->sampling_frequency * fmt->block_align;
		ret=true;
		break;
	}
	memFree(cdata);
	return ret;
}

/*
 *	Member functions for wave I/O port.
 */

/*
 *	port::read() - reads next block of data from file
 */
V_LOCAL	vsize_t	V_CALL
read_wav_file(vhandle_t handle,void *dest,vsize_t len)
{
	vsize_t rlen;
	wave_data_t *self;
	self=(wave_data_t*)handle;
	if(self->loop)
	{
		vsize_t alen;
		/* length after current position */
		rlen=self->num_bytes - self->transferred;
		if(rlen > len)
			rlen=len;
		alen=len-rlen;
		/* Read block */
		if(rlen)
		{
			rlen=fileRead(self->df,dest,rlen);
		    self->transferred += rlen;
		    dest = ((u8_t*)dest) + rlen;
		}
		if(alen)
		{
			alen=fileRead(self->df,dest,alen);
			fileSeek(self->df,self->data_offset,SEEK_SET);
		    self->transferred = alen;
		}
		return rlen + alen;
	}
	else
	{
		/* Remaining length of data */
		rlen = self->num_bytes - self->transferred;
		if(rlen < len)
			len=rlen;
		/* Read block */
		if(!len)
			return 0;
		rlen = fileRead(self->df,dest,len);
		self->transferred += rlen;
	}
	return rlen;
}

/*
 *	port::write() - writes next block of data to file
 */
V_LOCAL vsize_t V_CALL
write_wav_file(vhandle_t handle,void *src,vsize_t len)
{
	vsize_t wlen;
	wave_data_t *self;
	self = (wave_data_t*)handle;
	wlen = fileWrite(self->df,src,len);
	self->transferred += wlen;
	return wlen;
}

/*
 *	port::tell() - returns offset (in bytes) from the start of waveform data
 */
V_LOCAL vsize_t V_CALL
tell_wav_file(vhandle_t handle)
{
	wave_data_t *self = (wave_data_t*)handle;
	return fileTell(self->df) - self->data_offset;
}

/*
 *	port::tell() - sets offset (in bytes) from the start of waveform data
 */
V_LOCAL vsize_t V_CALL
seek_wav_file(vhandle_t handle,vssize_t offset,int whence)
{
	wave_data_t *self = (wave_data_t*)handle;
	vssize_t block_end = self->data_offset + self->num_bytes;
	switch(whence)
	{
		case SEEK_SET:
			offset += self->data_offset;
			break;
		case SEEK_END:
			offset += block_end;
			break;
		default:
			break;
	}
	if(offset < 0 || (vsize_t)offset < self->data_offset)
		offset = self->data_offset;
	else
	if(offset > block_end)
		offset = block_end;
	offset = fileSeek(self->df,offset,SEEK_SET);
	self->transferred = offset - self->data_offset;
	return self->transferred;
}

/*
 *	port::ioctl() - handles requests for parameters
 */
V_LOCAL int V_CALL
ioctl_wav_file(vhandle_t handle,unsigned code,void *data)
{
	u32_t pt;
	void *wavedata_base;
	wave_data_t *self = (wave_data_t*)handle;
	switch(code)
	{
		case pctl_get_type:
			if(!data)
				return -1;
			fileControl(self->df,pctl_get_type,&pt);
			*((u32_t*)data) = pt;
			break;
		case pctl_is_eof:
        	if(!data)
            	return -1;
			if(self->writemode)
				return -1;
			*((vboolean*)data) = self->num_bytes == self->transferred;
			break;
		case pctl_wave_get_num_channels:
			if(!data)
				return -1;
			*((u32_t*)data)=self->num_channels;
			break;
		case pctl_wave_get_sampling_frequency:
			if(!data)
				return -1;
			*((u32_t*)data)=self->sampling_frequency;
			break;
		case pctl_wave_get_bits_per_sample:
			if(!data)
				return -1;
			*((u32_t*)data)=self->bits_per_sample;
			break;
		case pctl_wave_get_num_samples:
			if(!data)
				return -1;
			*((vsize_t*)data)=self->num_samples;
			break;
		case pctl_wave_loop:
			if(!data)
				return -1;
			self->loop = (data) ? true : false;
			break;
		case pctl_map_ispace:
			if(!data)
				return -1;
			fileControl(self->df,pctl_map_ispace,&wavedata_base);
			*((void**)data) = (u8_t*)wavedata_base + self->data_offset;
			break;
		default:
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"ioctl_wav_file(%u,%0X): unknown ioctl code '%u'.\n",
				code,data,code);
#endif		/* CHECKED_BUILD */
			return -1;
	}
	return 0;
}

/*
 *	Closes wave file, frees all resources.
 */
V_LOCAL u32_t V_CALL
close_wav_file(vhandle_t handle)
{
	wave_data_t *self;
	self=(wave_data_t*)handle;
	if(--self->port.base.ref_count)
		return self->port.base.ref_count;
	if(self->df != NULL)
	{
		if(self->writemode)
		{
			/*
			 * File was opened for output: now we must write
			 * some information to the header.
			 */
			fileSeek(self->df,0x04,SEEK_SET);
			/* Write size of main chunk */
			filePutLittleLong(self->df,0x24 + self->transferred);
			fileWrite(self->df,"WAVEfmt ",8);
			/* Write size of 'data' chunk */
			fileSeek(self->df,0x28,SEEK_SET);
			filePutLittleLong(self->df,self->transferred);
		}
		fileClose(self->df);
	}
	memFreeObject(self);
	return 0;
}

/*
 *	Public functions: two constructors and several wrappers.
 */

/*
 *	Starts reading from wav file. Returned handle than can be
 *	passed to fileRead() and fileClose().
 */
vhandle_t	V_GLOBAL
wavOpenRead(char *name)
{
	char type_id[4];
	int chunk_count;
	wave_data_p self;
	chunk_head_t head;
	self=(wave_data_p)ioAllocPort(sizeof(wave_data_t));
	self->port.base.destroy=close_wav_file;
	self->port.read=read_wav_file;
	self->port.seek=seek_wav_file;
	self->port.tell=tell_wav_file;
	self->port.ioctl=ioctl_wav_file;
	self->df=fileOpenRead(name);
	self->writemode=false;
	if(self->df == LIBV_INVALID_HANDLE)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"waveOpenRead('%s'): unable to open file.\n",
			strSafe(name));
#endif	/* CHECKED_BUILD */
		memFreeObject(self);
		return LIBV_INVALID_HANDLE;
	}
	/* Read main header */
	if(!riff_read_head(self->df,&head))
	{
		fileClose(self->df);
		memFreeObject(self);
		return LIBV_INVALID_HANDLE;
	}
	/* Is it a valid RIFF file ? */
	if(memcmp(head.id,"RIFF",4))
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"waveOpenRead('%s'): expected RIFF fourcc, found '%c%c%c%c'.\n",
			name,head.id[0],head.id[1],head.id[2],head.id[3]);
#endif	/* CHECKED_BUILD */
		fileClose(self->df);
		memFreeObject(self);
		return LIBV_INVALID_HANDLE;
	}
	/* Read type_id */
	fileRead(self->df,type_id,4);
	/* Check for type of data */
	if(memcmp(type_id,"WAVE",4))
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"waveOpenRead('%s'): expected WAVE data, found '%s'.\n",
			name,type_id);
#endif	/* CHECKED_BUILD */
		fileClose(self->df);
		memFreeObject(self);
		return LIBV_INVALID_HANDLE;
	}
	/* Find 'fmt ' and 'data' chunks */
	for(chunk_count=0;chunk_count<2;)
	{
		if(!riff_read_head(self->df,&head))
		{
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_ERROR,"waveOpenRead('%s'): unexpected EOF.\n",
				name);
#endif	/* CHECKED_BUILD */
			break;
		}
		if(!memcmp(head.id,"fmt ",4)) /* read wave format */
		{
			if(!riff_read_fmt(self->df,&head,self))
				break;
			chunk_count++;
			continue;
		} else if(!memcmp(head.id,"data",4))
		{
			/* save parameters of data block */
			self->data_offset = fileTell(self->df);
			self->num_bytes = head.len;
			self->tail_garbage_length = fileSize(self->df) - (self->data_offset+self->num_bytes);
			/* Skip data block; data will be read later */
			fileSeek(self->df,head.len,SEEK_CUR);
			chunk_count++;
			continue;
		} else /* skip other chunks */
		{
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_TRACE,
				"waveOpenRead('%s'): unknown chunk '%c%c%c%c'; ignored.\n",
				name,head.id[0],head.id[1],head.id[2],head.id[3]);
#endif		/* CHECKED_BUILD */
			fileSeek(self->df,head.len,SEEK_CUR);
		}
	}
	if(chunk_count < 2)
	{
		/* we are failed to read both needed chunks */
		fileClose(self->df);
		memFreeObject(self);
		return LIBV_INVALID_HANDLE;
	}
	/* adjust number of samples and data size */
	self->num_samples = self->num_bytes / self->block_align;
	/* Seek to start of data */
	fileSeek(self,0,SEEK_SET);
	return self;
}

/*
 *	Starts writing to wav file. Returned handle than can be
 *	passed to fileWrite() and fileClose().
 */
vhandle_t	V_GLOBAL
wavOpenWrite(char *name,
	u32_t num_channels,
	u32_t sampling_frequency,
	u32_t bits_per_sample)
{
	wave_data_p self;
	self=(wave_data_p)ioAllocPort(sizeof(wave_data_t));
	self->port.base.destroy=close_wav_file;
	self->port.write=write_wav_file;
	self->port.ioctl=ioctl_wav_file;
	self->df=fileOpenWrite(name);
	if(self->df == NULL)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"waveOpenWrite('%s'): can't create file.\n",
			strSafe(name));
#endif	/* CHECKED_BUILD */
		__libvCloseHandle(self);
		return NULL;
	}
	self->writemode=true;
	self->num_channels=(u16_t)num_channels;
	self->sampling_frequency=(u32_t)sampling_frequency;
	self->bits_per_sample=(u16_t)bits_per_sample;
	self->block_align=
		(u16_t)(self->num_channels * (self->bits_per_sample >> 3));
	self->bytes_per_second=self->sampling_frequency * self->block_align;
	/* Write main header */
	fileWrite(self->df,"RIFF",4);
	/* Now we cant determine all data in header, so it 
	 * must be fixed later */
	filePutLittleLong(self->df,0x24);
	/* Write type_id and id of 'fmt ' chunk */
	fileWrite(self->df,"WAVEfmt ",8);
	/* Write size of fmt chunk */
	filePutLittleLong(self->df,0x10);
	/* Write wave parameters */
	filePutLittleShort(self->df,WAVE_FORMAT_PCM);
	filePutLittleShort(self->df,self->num_channels);
	filePutLittleLong(self->df,self->sampling_frequency);
	filePutLittleLong(self->df,self->bytes_per_second);
	filePutLittleShort(self->df,self->block_align);
	filePutLittleShort(self->df,self->bits_per_sample);
	/* Write id of 'data' chunk */
	fileWrite(self->df,"data",4);
	/* Write size of 'data' chunk */
	filePutLittleLong(self->df,0);
	return self;
}

/*
**	Handy wrappers
*/
u32_t	V_GLOBAL
wavNumChannels(vhandle_t handle)
{
	u32_t num_channels=0;
	fileControl(handle,pctl_wave_get_num_channels,&num_channels);
	return num_channels;
}

u32_t	V_GLOBAL
wavSamplingFrequency(vhandle_t handle)
{
	u32_t freq=0;
	fileControl(handle,pctl_wave_get_sampling_frequency,&freq);
	return freq;
}

u32_t	V_GLOBAL
wavBitsPerSample(vhandle_t handle)
{
	u32_t bps=0;
	fileControl(handle,pctl_wave_get_bits_per_sample,&bps);
	return bps;
}

u32_t	V_GLOBAL
wavNumSamples(vhandle_t handle)
{
	u32_t num_samples=0;
	fileControl(handle,pctl_wave_get_num_samples,&num_samples);
	return num_samples;
}

/*
 *	Reads all wave data into memAlloc()'ed buffer.
 */
void_p	V_GLOBAL
wavLoadFile(char *name,
	u32_t *nsamples_ret,
	u32_t *nchannels_ret,
	u32_t *freq_ret,
	u32_t *bps_ret)
{
	void *buffer;
	vsize_t nsamples, len;
	u32_t freq,nchannels,bps;
	vhandle_t wf;
	wf = wavOpenRead(name);
	if(wf == LIBV_INVALID_HANDLE)
		return NULL;
	/* Get wave parameters */
	if(!fileControl(wf,pctl_wave_get_sampling_frequency,&freq) ||
		!fileControl(wf,pctl_wave_get_bits_per_sample,&bps) ||
		!fileControl(wf,pctl_wave_get_num_samples,&nsamples) ||
		!fileControl(wf,pctl_wave_get_num_channels,&nchannels) )
	{
		fileClose(wf);
		return NULL;
	}
	len = ((freq * nchannels * bps) >> 3) * nsamples;
	/* Now allocate memory and read wave data */
	buffer=(u8_t*)memAlloc(len);
	if(buffer == NULL)
		return NULL;
	fileRead(wf,buffer,len);
	fileClose(wf);
	if(nsamples_ret)
		*nsamples_ret=nsamples;
	if(nchannels_ret)
		*nchannels_ret=nchannels;
	if(freq_ret)
		*freq_ret=freq;
	if(bps_ret)
		*bps_ret=bps;
	return buffer;
}

#ifdef	CONFIG_AUDIO_MP3

/*
 * This is a collection of hacks.
 * The mp3 library currently is not reentrant, so I've decided
 * to leave MPlayer's interface unchanged.
 */

#define	MP3_IN_BUF_SIZE		16384
#define	MP3_OUT_BUF_SIZE	16384

typedef struct mp3_decoder_s
{
	ioport_base_t	vtbl;
	u32_t			num_channels, sampling_frequency, bits_per_sample;
	u8_fifo_t		fifo;
}	mp3_decoder_t, *mp3_decoder_p;

vboolean mp3_loop = false;
vboolean mp3_decode_in_progress = false;
vhandle_t mp3_input_handle = LIBV_INVALID_HANDLE;
#define MP3_BUFFLEN 4608
static unsigned char mp3_buffer[MP3_BUFFLEN];

/*
 * Routine to call from mp3 library
 */
int LibV_audio_read(char *buf,int size)
{
	vsize_t len = fileRead(mp3_input_handle,buf,size);
    if(!len)
    {
		if(mp3_loop)
        {
	    	fileSeek(mp3_input_handle,0,SEEK_SET);
			len = fileRead(mp3_input_handle,buf,size);
		}
	}
	return (int)len;
}

V_LOCAL	vsize_t V_CALL
mp3_read(vhandle_t handle,void *buf,vsize_t len)
{
	int decoded;
	mp3_decoder_t *dec=(mp3_decoder_t *)handle;
	while(u8fCount(&dec->fifo) < len)
	{
		decoded = MP3_DecodeFrame(mp3_buffer,-1);
		if(decoded <= 0) /* EOF */
			break;
		u8fPushBuffer(&dec->fifo,mp3_buffer,(u32_t)decoded);
	}
	if(len > u8fCount(&dec->fifo))
		len = u8fCount(&dec->fifo);
	if(len)
		u8fPopBuffer(&dec->fifo,(u8_t*)buf,len);
	return len;
}

#include <../source/PAL/argsused.h>
V_LOCAL vsize_t V_CALL
mp3_seek(vhandle_t handle,vssize_t offset,int whence)
{
	if(offset || whence != SEEK_SET)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"mp3_seek(): only rewind operation is supported.\n");
#endif	/* CHECKED_BUILD */
		return 0; /* BUGBUG ??? */
	}
	return fileSeek(mp3_input_handle,0,SEEK_SET);
}

V_LOCAL u32_t V_CALL
mp3_close(vhandle_t handle)
{
	mp3_decoder_p dec=(mp3_decoder_p)handle;
	if(--dec->vtbl.base.ref_count)
		return dec->vtbl.base.ref_count;
	u8fClear(&dec->fifo);
	fileClose(mp3_input_handle);
	memFreeObject(handle);
	mp3_input_handle = LIBV_INVALID_HANDLE;
	mp3_decode_in_progress = false;
	return 0;
}

V_LOCAL int V_CALL
mp3_ioctl(vhandle_t handle,unsigned code,void *data)
{
	mp3_decoder_p self=(mp3_decoder_p)handle;
	switch(code)
	{
		case pctl_wave_get_num_channels:
			if(!data)
				return -1;
			*((u32_t*)data)=self->num_channels;
			break;
		case pctl_wave_get_sampling_frequency:
			if(!data)
				return -1;
			*((u32_t*)data)=self->sampling_frequency;
			break;
		case pctl_wave_get_bits_per_sample:
			if(!data)
				return -1;
			*((u32_t*)data)=self->bits_per_sample;
			break;
		case pctl_wave_loop:
			if(!data || !*((u32_p)data))
				mp3_loop = false;
			else
            	mp3_loop = true;
			break;
		default:
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_ERROR,
				"mp3_ioctl(%u,%0X): unknown ioctl code '%u'.\n",
				code,data,code);
#endif		/* CHECKED_BUILD */
			return -1;
	}
	return 0;
}

vhandle_t V_GLOBAL
mp3Open(char *name)
{
	int len;
	vhandle_t in;
	mp3_decoder_p dec;
	unsigned char id[4];
	vboolean need_read_head;
	u32_t num_channels=0, sampling_frequency=0, bits_per_sample=0;
	if(mp3_decode_in_progress)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"mp3Open(%s): unable to decode more than one file at time.\n",name);
#endif	/* CHECKED_BUILD */
		return LIBV_INVALID_HANDLE;
	}
	mp3_decode_in_progress=true;
	in=fileOpenRead(name);
	if(in == LIBV_INVALID_HANDLE)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"mp3Open(%s): unable to open.\n",name);
#endif	/* CHECKED_BUILD */
		mp3_decode_in_progress=false;
		return LIBV_INVALID_HANDLE;
	}
	fileRead(in,id,4);
	if(!memcmp(id,"RIFF",4))
	{
		fileClose(in);
		in=wavOpenRead(name);
		if(in == LIBV_INVALID_HANDLE)
		{
			mp3_decode_in_progress=false;
			return LIBV_INVALID_HANDLE;
		}
		fileControl(in,pctl_wave_get_num_channels,&num_channels);
		fileControl(in,pctl_wave_get_sampling_frequency,&sampling_frequency);
		fileControl(in,pctl_wave_get_bits_per_sample,&bits_per_sample);
		need_read_head = 0;
	}
	else
	{
		need_read_head = 1;
		fileSeek(in,0,SEEK_SET);
	}
	MP3_Init();
	MP3_samplerate = MP3_channels = MP3_bps = 0;
	mp3_input_handle = in;
	if(need_read_head)
	{
		len = MP3_DecodeFrame(mp3_buffer,-1);
		if(len <= 0)
		{
#ifdef		CHECKED_BUILD
			conlPrintf(LOG_ERROR,"mp3Open(%s):invalid or corrupted MP3 file.\n",name);
#endif		/* CHECKED_BUILD */
			mp3_input_handle = LIBV_INVALID_HANDLE;
			fileClose(in);
			mp3_decode_in_progress=false;
			return LIBV_INVALID_HANDLE;
		}
		fileSeek(in,0,SEEK_SET);
		num_channels = MP3_channels;
		sampling_frequency = MP3_samplerate;
		bits_per_sample = 16; /* MP3_bps; FIXME */
	}
	dec=(mp3_decoder_p)ioAllocPort(sizeof(mp3_decoder_t));
	dec->vtbl.base.destroy=mp3_close;
	dec->vtbl.read=mp3_read;
	dec->vtbl.seek=mp3_seek;
	dec->vtbl.ioctl=mp3_ioctl;
	dec->num_channels=num_channels;
	dec->sampling_frequency=sampling_frequency;
	dec->bits_per_sample=bits_per_sample;
	u8fInit(&dec->fifo);
	return (vhandle_t)dec;
}

#endif	/* CONFIG_AUDIO_MP3 */
