#include "stdafx.h"
#pragma hdrstop

#include <msacm.h>

#include "soundrender_core.h"
#include "soundrender_source.h"

//	SEEK_SET	0	File beginning
//	SEEK_CUR	1	Current file pointer position
//	SEEK_END	2	End-of-file
int ov_seek_func(void *datasource, s64 offset, int whence)	
{
	switch (whence){
	case SEEK_SET: ((IReader*)datasource)->seek((int)offset);	 break;
	case SEEK_CUR: ((IReader*)datasource)->advance((int)offset); break;
	case SEEK_END: ((IReader*)datasource)->seek((int)offset + ((IReader*)datasource)->length()); break;
	}
	return 0; 
}
size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
{ 
	IReader* F			= (IReader*)datasource; 
	size_t exist_block	= _max(0ul,iFloor(F->elapsed()/(float)size));
	size_t read_block	= _min(exist_block,nmemb);
	F->r				(ptr,(int)(read_block*size));	
	return read_block;
}
int ov_close_func(void *datasource)									
{	
	return 0; 
}
long ov_tell_func(void *datasource)									
{	
	return ((IReader*)datasource)->tell(); 
}

void CSoundRender_Source::decompress(u32 line, OggVorbis_File* ovf)
{
	VERIFY	(ovf);
	// decompression of one cache-line
	u32		line_size		= SoundRender->cache.get_linesize();
	char*	dest			= (char*)SoundRender->cache.get_dataptr	(CAT,line);
	u32		buf_offs		= (line*line_size) / 2 / m_wformat.nChannels;
	u32		left_file		= dwBytesTotal - buf_offs;
	u32		left			= (u32)_min	(left_file,line_size);

	// seek
	u32	cur_pos				= u32(ov_pcm_tell(ovf));
	if (cur_pos!=buf_offs)
		ov_pcm_seek			(ovf,buf_offs);

	// decompress
	i_decompress_fr(ovf,dest,left);
}

void CSoundRender_Source::LoadWave	(LPCSTR pName)
{
	pname					= pName;

	// Load file into memory and parse WAV-format
	OggVorbis_File			ovf;
	ov_callbacks ovc		= {ov_read_func,ov_seek_func,ov_close_func,ov_tell_func};
	IReader* wave			= FS.r_open		(pname.c_str()); 
	R_ASSERT3				(wave&&wave->length(),"Can't open wave file:",pname.c_str());
	ov_open_callbacks		(wave,&ovf,NULL,0,ovc);

	vorbis_info* ovi		= ov_info(&ovf,-1);
	// verify
	R_ASSERT3				(ovi,"Invalid source info:",pName);
	R_ASSERT3				(ovi->rate==44100,"Invalid source rate:",pName);

#ifdef DEBUG
	if(ovi->channels==2)
	{
		Msg("stereo sound source [%s]", pName);
	}
#endif // #ifdef DEBUG

	ZeroMemory					( &m_wformat, sizeof( WAVEFORMATEX ) );

	m_wformat.nSamplesPerSec	= (ovi->rate); //44100;
	m_wformat.wFormatTag		= WAVE_FORMAT_PCM;
	m_wformat.nChannels			= u16(ovi->channels);
	m_wformat.wBitsPerSample	= 16;

	m_wformat.nBlockAlign		= m_wformat.wBitsPerSample / 8 * m_wformat.nChannels;
	m_wformat.nAvgBytesPerSec	= m_wformat.nSamplesPerSec * m_wformat.nBlockAlign;

	s64 pcm_total				= ov_pcm_total(&ovf,-1);
	dwBytesTotal				= u32(pcm_total*m_wformat.nBlockAlign); 
	fTimeTotal					= s_f_def_source_footer + dwBytesTotal/float(m_wformat.nAvgBytesPerSec);

	vorbis_comment*	ovm		= ov_comment(&ovf,-1);
	if (ovm->comments)
	{
		IReader F			(ovm->user_comments[0],ovm->comment_lengths[0]);
		u32 vers			= F.r_u32	();
        if (vers==0x0001){
			m_fMinDist		= F.r_float	();
			m_fMaxDist		= F.r_float	();
	        m_fBaseVolume	= 1.f;
			m_uGameType		= F.r_u32	();
			m_fMaxAIDist	= m_fMaxDist;
		}else if (vers==0x0002){
			m_fMinDist		= F.r_float	();
			m_fMaxDist		= F.r_float	();
			m_fBaseVolume	= F.r_float	();
			m_uGameType		= F.r_u32	();
			m_fMaxAIDist	= m_fMaxDist;
		}else if (vers==OGG_COMMENT_VERSION){
			m_fMinDist		= F.r_float	();
			m_fMaxDist		= F.r_float	();
            m_fBaseVolume	= F.r_float	();
			m_uGameType		= F.r_u32	();
			m_fMaxAIDist	= F.r_float	();
		}else{
			Log				("! Invalid ogg-comment version, file: ",pName);
		}
	}else{
		Log					("! Missing ogg-comment, file: ",pName);
	}
	R_ASSERT3((m_fMaxAIDist>=0.1f)&&(m_fMaxDist>=0.1f),"Invalid max distance.",pName);

	ov_clear				(&ovf);
	FS.r_close				(wave);
}

void CSoundRender_Source::load(LPCSTR name)
{
	string_path			fn,N;
	xr_strcpy				(N,name);
	strlwr				(N);
	if (strext(N))		*strext(N) = 0;

	fname				= N;

	strconcat			(sizeof(fn),fn,N,".ogg");
	if (!FS.exist("$level$",fn))	FS.update_path	(fn,"$game_sounds$",fn);

#ifdef _EDITOR
	if (!FS.exist(fn)){ 
		FS.update_path	(fn,"$game_sounds$","$no_sound.ogg");
    }
#endif
	LoadWave			(fn);
	SoundRender->cache.cat_create	(CAT, dwBytesTotal);
}

void CSoundRender_Source::unload()
{
	SoundRender->cache.cat_destroy	(CAT);
    fTimeTotal						= 0.0f;
    dwBytesTotal					= 0;
}

