//-----------------------------------------------------------------------------
//
//  $Logfile:: /Source/snd_mem.c                                              $
// $Revision:: 3                                                              $
//   $Author:: Burgermac                                                      $
//     $Date:: 8/8/01 4:49 PM                                                 $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// DESCRIPTION:
// sound caching
//

#include "client.h"
#include "snd_loc.h"
#include "wave.h"

/*
================
ReformatStereoIMAADPCM_Sfx
================
*/
static void ReformatStereoIMAADPCM_Sfx(sfx_t *sfx, byte *data)
{
	int blockcount;
	int subblockcount;
	sfxcache_t *sc;
	byte *dest;
	
	sc = sfx->cache;
	if (!sc)
		return;

	blockcount = sc->real_length / sc->blockalign;
	if (blockcount * sc->blockalign != sc->real_length)
	{
		Com_DPrintf (DEVELOPER_MSG_SOUND, "%s is not an even block multiple\n",sfx->name);
	}
	subblockcount = (sc->blockalign - (sizeof(unsigned) * 2)) >> 3;
	dest = sc->data;
	while (blockcount--)
	{
		int i;
		int j;
		//
		// copy over the block header
		//
		memcpy(dest, data, sizeof(unsigned) * 2);
		data += sizeof(unsigned) * 2;
		dest += sizeof(unsigned) * 2;
		for (j = 0; j < subblockcount; j++)
		{
			unsigned dwLeft;
			unsigned dwRight;
			//
			// grab each 8 samples
			//
			dwLeft	= LittleLong(*(unsigned *)data);
			data	+= sizeof(unsigned);
			dwRight	= LittleLong(*(unsigned *)data);
			data	+= sizeof(unsigned);

			for (i = 8; i > 0; i--)
			{
				int leftcode;
				int rightcode;

				leftcode	= (int)(dwLeft & 0x0F);
				rightcode	= (int)(dwRight & 0x0F);
				*(dest++)	= leftcode + (rightcode<<4);

				dwLeft >>= 4;
				dwRight >>= 4;
			}
		}
	}
}

/*
================
ResampleSfx
================
*/
static void ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte *data)
{
	int outcount;
	int i;
	int sample;
	sfxcache_t *sc;

	sc = sfx->cache;
	if (!sc)
		return;

	outcount = sc->real_length;
	if (sc->type == WAV_IMA_ADPCM_FORMAT)
	{
		if (sc->stereo)
		{
			ReformatStereoIMAADPCM_Sfx(sfx, data);
			//memcpy(sc->data, data, outcount);
		}
		else
		{
			// no need to reformat
			memcpy(sc->data, data, outcount);
		}
		return;
	}
	else
	{
		if (s_loadas8bit->value)
			sc->width = 1;
		else
			sc->width = inwidth;

		// resample / decimate to the current source rate
		if (inwidth == 1 && sc->width == 1)
		{
			// fast special case
			for (i = 0; i < outcount; i++)
				((signed char *)sc->data)[i] = (int)((unsigned char)(data[i]) - 128);
		}
		else
		{
			// general case
			for (i=0 ; i<outcount ; i++)
			{
				if (inwidth == 2)
					sample = LittleShort( ((signed short *)data)[i]);
				else
					sample = (int) ((unsigned char)(data[i]) - 128) << 8;
				if (sc->width == 2)
					((signed short *)sc->data)[i] = sample;
				else
					((signed char *)sc->data)[i] = sample >> 8;
			}
		}
	}
}

//=============================================================================

/*
==============
S_LoadRaw
==============
*/
sfxcache_t *S_LoadRaw (int khz, int bits, int channels, int length, byte * data)
{
	sfx_t sfx;
	wavinfo_t info;
	int len;
	int real_length;
	sfxcache_t *sc;

	info.loopstart = -1;
	info.rate = khz;
	info.blockalign = 0;
	info.format = WAV_PCM_FORMAT;
	info.channels = channels;
	info.width = bits / 8;
	info.samples = length;

	len = info.samples;
	info.samples /= info.width;
	real_length = info.samples;
	info.samples /= info.channels;

	sc = (sfxcache_t *)Z_Malloc(len + sizeof(sfxcache_t));
	if (!sc)
		return NULL;

	sc->length = info.samples;
	sc->loopstart = info.loopstart;
	sc->speed = info.rate;
	sc->width = info.width;
	sc->stereo = info.channels-1;
	sc->type = info.format;
	sc->blockalign = info.blockalign;
	sc->real_length = real_length;

	sfx.cache = sc;
	ResampleSfx (&sfx, sc->speed, sc->width, data);

	return sc;
}


/*
==============
S_FreeSound
==============
*/
void S_FreeSound (sfx_t *s)
{
	Z_Free(s->cache);
	s->cache = NULL;
}

/*
==============
S_LoadSound
==============
*/
static sfxcache_t *S_LoadSoundPath (sfx_t *s, const char *namebuffer)
{
	void	*data;
	wavinfo_t	info;
	int	len;
	int	real_length;
	sfxcache_t	*sc;
	int	size;

	// see if still in memory
	sc = s->cache;
	if (sc)
		return sc;

	size = FS_LoadFile (namebuffer, &data);
	if (!data)
	{
		Com_DPrintf (DEVELOPER_MSG_IO, "Couldn't load %s\n", namebuffer);
		return NULL;
	}

	info = GetWavinfo (s->name, (byte *)data, size);
	if (!info.format || !info.channels)
		return NULL;

	if (info.format == WAV_PCM_FORMAT)
	{
		len = info.samples;
		info.samples /= info.width;
		real_length = info.samples;
		info.samples /= info.channels;
	}
	else if (info.format == WAV_IMA_ADPCM_FORMAT)
	{
		int numblocks;

		len = info.samples;
		real_length = info.samples;
		// determine number of blocks
		numblocks = info.samples/info.blockalign;
		// subtrack block headers from real_length
		info.samples -= numblocks*info.channels*sizeof(unsigned);
		info.samples *= 2/info.channels;
	}
	else {
		goto fail; /* not reached */
	}

	sc = s->cache = (sfxcache_t *) Z_Malloc(len + sizeof(sfxcache_t));
	if (!sc) {
	fail:
		FS_FreeFile (data);
		return NULL;
	}

	sc->length = info.samples;
	sc->loopstart = info.loopstart;
	sc->speed = info.rate;
	sc->width = info.width;
	sc->stereo = info.channels-1;
	sc->type = info.format;
	sc->blockalign = info.blockalign;
	sc->real_length = real_length;

	ResampleSfx (s, sc->speed, sc->width, (byte *)data + info.dataofs);

	FS_FreeFile (data);

	return sc;
}

sfxcache_t *S_LoadSound (sfx_t *s)
{
	char namebuffer[MAX_QPATH];

	// see if still in memory
	if (s->cache)
		return s->cache;

	Com_sprintf (namebuffer, sizeof(namebuffer), "%s/%s", s_sounddir->string, s->name);
	S_LoadSoundPath(s, namebuffer);
	return s->cache;
}

sfxcache_t *S_LoadSong (sfx_t *s)
{
	char namebuffer[MAX_QPATH];

	// see if still in memory
	if (s->cache)
		return s->cache;

	Com_sprintf (namebuffer, sizeof(namebuffer), "%s/%s", s_musicdir->string, s->name);
	S_LoadSoundPath(s, namebuffer);
	return s->cache;
}
